2015-05-13 12:03:31 +02:00
// Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2016-11-21 15:51:36 +01:00
package file
2015-05-13 12:03:31 +02:00
import (
2017-10-24 21:21:42 -07:00
"context"
2015-05-13 12:03:31 +02:00
"encoding/json"
2022-06-03 13:47:14 +02:00
"errors"
2015-05-13 12:03:31 +02:00
"fmt"
2022-04-27 11:24:36 +02:00
"io"
2024-09-09 21:41:53 -04:00
"log/slog"
2017-08-12 22:01:31 +05:30
"os"
2015-05-13 12:03:31 +02:00
"path/filepath"
"strings"
2017-11-30 23:54:40 -08:00
"sync"
2015-05-13 12:03:31 +02:00
"time"
2021-11-17 13:07:14 +01:00
"github.com/fsnotify/fsnotify"
2022-02-13 00:58:27 +01:00
"github.com/grafana/regexp"
2016-10-21 12:12:19 +02:00
"github.com/prometheus/client_golang/prometheus"
2020-08-20 05:48:26 -07:00
"github.com/prometheus/common/config"
2015-08-22 09:42:45 +02:00
"github.com/prometheus/common/model"
2024-09-09 21:41:53 -04:00
"github.com/prometheus/common/promslog"
2022-08-31 21:50:38 +08:00
"gopkg.in/yaml.v2"
2019-03-25 16:01:12 -07:00
2020-08-20 05:48:26 -07:00
"github.com/prometheus/prometheus/discovery"
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
"github.com/prometheus/prometheus/discovery/targetgroup"
)
var (
patFileSDName = regexp . MustCompile ( ` ^[^*]*(\*[^/]*)?\.(json|yml|yaml|JSON|YML|YAML)$ ` )
2015-05-13 12:03:31 +02:00
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
// DefaultSDConfig is the default file SD configuration.
DefaultSDConfig = SDConfig {
RefreshInterval : model . Duration ( 5 * time . Minute ) ,
}
2015-05-13 12:03:31 +02:00
)
2020-08-20 05:48:26 -07:00
func init ( ) {
discovery . RegisterConfig ( & SDConfig { } )
}
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
// SDConfig is the configuration for file based discovery.
type SDConfig struct {
Files [ ] string ` yaml:"files" `
RefreshInterval model . Duration ` yaml:"refresh_interval,omitempty" `
}
2024-01-23 15:53:55 +00:00
// NewDiscovererMetrics implements discovery.Config.
func ( * SDConfig ) NewDiscovererMetrics ( reg prometheus . Registerer , rmi discovery . RefreshMetricsInstantiator ) discovery . DiscovererMetrics {
return newDiscovererMetrics ( reg , rmi )
}
2020-08-20 05:48:26 -07:00
// Name returns the name of the Config.
func ( * SDConfig ) Name ( ) string { return "file" }
// NewDiscoverer returns a Discoverer for the Config.
func ( c * SDConfig ) NewDiscoverer ( opts discovery . DiscovererOptions ) ( discovery . Discoverer , error ) {
2024-01-23 15:53:55 +00:00
return NewDiscovery ( c , opts . Logger , opts . Metrics )
2020-08-20 05:48:26 -07:00
}
// SetDirectory joins any relative file paths with dir.
func ( c * SDConfig ) SetDirectory ( dir string ) {
for i , file := range c . Files {
c . Files [ i ] = config . JoinDir ( dir , file )
}
}
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func ( c * SDConfig ) UnmarshalYAML ( unmarshal func ( interface { } ) error ) error {
* c = DefaultSDConfig
type plain SDConfig
err := unmarshal ( ( * plain ) ( c ) )
if err != nil {
return err
}
if len ( c . Files ) == 0 {
2019-03-25 16:01:12 -07:00
return errors . New ( "file service discovery config must contain at least one path name" )
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
}
for _ , name := range c . Files {
if ! patFileSDName . MatchString ( name ) {
2022-06-03 13:47:14 +02:00
return fmt . Errorf ( "path name %q is not valid for file discovery" , name )
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
}
}
return nil
}
2015-08-24 15:07:27 +02:00
const fileSDFilepathLabel = model . MetaLabelPrefix + "filepath"
2015-05-19 15:24:26 +02:00
2017-08-12 22:01:31 +05:30
// TimestampCollector is a Custom Collector for Timestamps of the files.
2024-01-23 15:53:55 +00:00
// TODO(ptodev): Now that each file SD has its own TimestampCollector
// inside discovery/file/metrics.go, we can refactor this collector
// (or get rid of it) as each TimestampCollector instance will only use one discoverer.
2017-08-12 22:01:31 +05:30
type TimestampCollector struct {
Description * prometheus . Desc
2017-11-30 23:54:40 -08:00
discoverers map [ * Discovery ] struct { }
lock sync . RWMutex
2017-08-12 22:01:31 +05:30
}
// Describe method sends the description to the channel.
func ( t * TimestampCollector ) Describe ( ch chan <- * prometheus . Desc ) {
ch <- t . Description
}
// Collect creates constant metrics for each file with last modified time of the file.
func ( t * TimestampCollector ) Collect ( ch chan <- prometheus . Metric ) {
2017-11-30 23:54:40 -08:00
// New map to dedup filenames.
uniqueFiles := make ( map [ string ] float64 )
t . lock . RLock ( )
for fileSD := range t . discoverers {
2018-01-28 22:24:31 +05:30
fileSD . lock . RLock ( )
2017-11-30 23:54:40 -08:00
for filename , timestamp := range fileSD . timestamps {
uniqueFiles [ filename ] = timestamp
2017-08-12 22:01:31 +05:30
}
2018-01-28 22:24:31 +05:30
fileSD . lock . RUnlock ( )
2017-11-30 23:54:40 -08:00
}
t . lock . RUnlock ( )
for filename , timestamp := range uniqueFiles {
2017-08-12 22:01:31 +05:30
ch <- prometheus . MustNewConstMetric (
t . Description ,
prometheus . GaugeValue ,
2017-11-30 23:54:40 -08:00
timestamp ,
filename ,
2017-08-12 22:01:31 +05:30
)
}
}
2017-11-30 23:54:40 -08:00
func ( t * TimestampCollector ) addDiscoverer ( disc * Discovery ) {
t . lock . Lock ( )
t . discoverers [ disc ] = struct { } { }
t . lock . Unlock ( )
}
func ( t * TimestampCollector ) removeDiscoverer ( disc * Discovery ) {
t . lock . Lock ( )
delete ( t . discoverers , disc )
t . lock . Unlock ( )
}
2017-08-12 22:01:31 +05:30
// NewTimestampCollector creates a TimestampCollector.
func NewTimestampCollector ( ) * TimestampCollector {
return & TimestampCollector {
Description : prometheus . NewDesc (
2018-01-22 14:02:24 +01:00
"prometheus_sd_file_mtime_seconds" ,
2017-11-30 23:54:40 -08:00
"Timestamp (mtime) of files read by FileSD. Timestamp is set at read time." ,
2017-08-12 22:01:31 +05:30
[ ] string { "filename" } ,
nil ,
) ,
2017-11-30 23:54:40 -08:00
discoverers : make ( map [ * Discovery ] struct { } ) ,
2017-08-12 22:01:31 +05:30
}
}
2017-03-16 20:29:47 -03:00
// Discovery provides service discovery functionality based
2015-05-13 12:03:31 +02:00
// on files that contain target groups in JSON or YAML format. Refreshing
// happens using file watches and periodic refreshes.
2017-03-16 20:29:47 -03:00
type Discovery struct {
2017-11-30 23:54:40 -08:00
paths [ ] string
watcher * fsnotify . Watcher
interval time . Duration
timestamps map [ string ] float64
lock sync . RWMutex
2015-05-13 12:03:31 +02:00
// lastRefresh stores which files were found during the last refresh
// and how many target groups they contained.
// This is used to detect deleted target groups.
lastRefresh map [ string ] int
2024-09-09 21:41:53 -04:00
logger * slog . Logger
2023-10-23 14:55:36 +01:00
2024-01-23 15:53:55 +00:00
metrics * fileMetrics
2015-05-13 12:03:31 +02:00
}
2016-11-21 15:51:36 +01:00
// NewDiscovery returns a new file discovery for the given paths.
2024-09-09 21:41:53 -04:00
func NewDiscovery ( conf * SDConfig , logger * slog . Logger , metrics discovery . DiscovererMetrics ) ( * Discovery , error ) {
2024-01-23 15:53:55 +00:00
fm , ok := metrics . ( * fileMetrics )
if ! ok {
2024-11-03 13:15:51 +01:00
return nil , errors . New ( "invalid discovery metrics type" )
2024-01-23 15:53:55 +00:00
}
2017-08-11 20:45:52 +02:00
if logger == nil {
2024-09-09 21:41:53 -04:00
logger = promslog . NewNopLogger ( )
2017-08-11 20:45:52 +02:00
}
2017-11-30 23:54:40 -08:00
disc := & Discovery {
paths : conf . Files ,
interval : time . Duration ( conf . RefreshInterval ) ,
timestamps : make ( map [ string ] float64 ) ,
logger : logger ,
2024-01-23 15:53:55 +00:00
metrics : fm ,
2015-05-13 12:03:31 +02:00
}
2023-10-23 14:55:36 +01:00
2024-01-23 15:53:55 +00:00
fm . init ( disc )
2023-10-23 14:55:36 +01:00
return disc , nil
2015-05-13 12:03:31 +02:00
}
// listFiles returns a list of all files that match the configured patterns.
2017-03-16 20:29:47 -03:00
func ( d * Discovery ) listFiles ( ) [ ] string {
2015-05-13 12:03:31 +02:00
var paths [ ] string
2017-03-16 20:29:47 -03:00
for _ , p := range d . paths {
2015-05-13 12:03:31 +02:00
files , err := filepath . Glob ( p )
if err != nil {
2024-09-09 21:41:53 -04:00
d . logger . Error ( "Error expanding glob" , "glob" , p , "err" , err )
2015-05-13 12:03:31 +02:00
continue
}
paths = append ( paths , files ... )
}
return paths
}
// watchFiles sets watches on all full paths or directories that were configured for
// this file discovery.
2017-03-16 20:29:47 -03:00
func ( d * Discovery ) watchFiles ( ) {
if d . watcher == nil {
2015-05-13 12:03:31 +02:00
panic ( "no watcher configured" )
}
2017-03-16 20:29:47 -03:00
for _ , p := range d . paths {
2023-06-23 16:37:04 -04:00
if dir , _ := filepath . Split ( p ) ; dir != "" {
p = dir
2015-05-13 12:03:31 +02:00
} else {
p = "./"
}
2017-03-16 20:29:47 -03:00
if err := d . watcher . Add ( p ) ; err != nil {
2024-09-09 21:41:53 -04:00
d . logger . Error ( "Error adding file watch" , "path" , p , "err" , err )
2015-05-13 12:03:31 +02:00
}
}
}
2018-01-08 15:59:18 -08:00
// Run implements the Discoverer interface.
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
func ( d * Discovery ) Run ( ctx context . Context , ch chan <- [ ] * targetgroup . Group ) {
2015-05-13 12:03:31 +02:00
watcher , err := fsnotify . NewWatcher ( )
if err != nil {
2024-09-09 21:41:53 -04:00
d . logger . Error ( "Error adding file watcher" , "err" , err )
2024-01-23 15:53:55 +00:00
d . metrics . fileWatcherErrorsCount . Inc ( )
2015-05-13 12:03:31 +02:00
return
}
2017-03-16 20:29:47 -03:00
d . watcher = watcher
2017-12-21 11:07:43 +01:00
defer d . stop ( )
2015-05-13 12:03:31 +02:00
2017-03-16 20:29:47 -03:00
d . refresh ( ctx , ch )
2015-05-13 12:03:31 +02:00
2017-03-16 20:29:47 -03:00
ticker := time . NewTicker ( d . interval )
2015-05-13 12:03:31 +02:00
defer ticker . Stop ( )
for {
select {
2016-02-18 17:26:27 +01:00
case <- ctx . Done ( ) :
2015-05-13 12:03:31 +02:00
return
2015-08-10 16:44:32 +02:00
2017-03-16 20:29:47 -03:00
case event := <- d . watcher . Events :
2016-11-18 10:55:29 +01:00
// fsnotify sometimes sends a bunch of events without name or operation.
// It's unclear what they are and why they are sent - filter them out.
if len ( event . Name ) == 0 {
break
}
// Everything but a chmod requires rereading.
if event . Op ^ fsnotify . Chmod == 0 {
break
}
// Changes to a file can spawn various sequences of events with
// different combinations of operations. For all practical purposes
// this is inaccurate.
// The most reliable solution is to reload everything if anything happens.
2017-03-16 20:29:47 -03:00
d . refresh ( ctx , ch )
2016-11-18 10:55:29 +01:00
case <- ticker . C :
// Setting a new watch after an update might fail. Make sure we don't lose
// those files forever.
2017-03-16 20:29:47 -03:00
d . refresh ( ctx , ch )
2016-11-18 10:55:29 +01:00
2017-03-16 20:29:47 -03:00
case err := <- d . watcher . Errors :
2016-11-18 10:55:29 +01:00
if err != nil {
2024-09-09 21:41:53 -04:00
d . logger . Error ( "Error watching file" , "err" , err )
2015-05-13 12:03:31 +02:00
}
}
}
}
2017-11-30 23:54:40 -08:00
func ( d * Discovery ) writeTimestamp ( filename string , timestamp float64 ) {
d . lock . Lock ( )
d . timestamps [ filename ] = timestamp
d . lock . Unlock ( )
}
func ( d * Discovery ) deleteTimestamp ( filename string ) {
d . lock . Lock ( )
delete ( d . timestamps , filename )
d . lock . Unlock ( )
}
2015-08-28 12:15:46 +02:00
// stop shuts down the file watcher.
2017-03-16 20:29:47 -03:00
func ( d * Discovery ) stop ( ) {
2024-09-09 21:41:53 -04:00
d . logger . Debug ( "Stopping file discovery..." , "paths" , fmt . Sprintf ( "%v" , d . paths ) )
2015-08-28 12:15:46 +02:00
done := make ( chan struct { } )
defer close ( done )
2024-01-23 15:53:55 +00:00
d . metrics . fileSDTimeStamp . removeDiscoverer ( d )
2017-11-30 23:54:40 -08:00
2015-08-28 12:15:46 +02:00
// Closing the watcher will deadlock unless all events and errors are drained.
go func ( ) {
for {
select {
2017-03-16 20:29:47 -03:00
case <- d . watcher . Errors :
case <- d . watcher . Events :
2015-08-28 12:15:46 +02:00
// Drain all events and errors.
case <- done :
return
}
}
} ( )
2017-03-16 20:29:47 -03:00
if err := d . watcher . Close ( ) ; err != nil {
2024-09-09 21:41:53 -04:00
d . logger . Error ( "Error closing file watcher" , "paths" , fmt . Sprintf ( "%v" , d . paths ) , "err" , err )
2015-09-13 01:06:40 +02:00
}
2015-08-28 12:15:46 +02:00
2024-09-09 21:41:53 -04:00
d . logger . Debug ( "File discovery stopped" )
2015-08-28 12:15:46 +02:00
}
2015-08-26 02:58:59 +02:00
// refresh reads all files matching the discovery's patterns and sends the respective
2015-05-13 12:03:31 +02:00
// updated target groups through the channel.
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
func ( d * Discovery ) refresh ( ctx context . Context , ch chan <- [ ] * targetgroup . Group ) {
2016-10-21 12:12:19 +02:00
t0 := time . Now ( )
defer func ( ) {
2024-01-23 15:53:55 +00:00
d . metrics . fileSDScanDuration . Observe ( time . Since ( t0 ) . Seconds ( ) )
2016-10-21 12:12:19 +02:00
} ( )
2015-05-13 12:03:31 +02:00
ref := map [ string ] int { }
2017-03-16 20:29:47 -03:00
for _ , p := range d . listFiles ( ) {
2017-11-30 23:54:40 -08:00
tgroups , err := d . readFile ( p )
2015-05-13 12:03:31 +02:00
if err != nil {
2024-01-23 15:53:55 +00:00
d . metrics . fileSDReadErrorsCount . Inc ( )
2017-08-11 20:45:52 +02:00
2024-09-09 21:41:53 -04:00
d . logger . Error ( "Error reading file" , "path" , p , "err" , err )
2015-05-13 12:03:31 +02:00
// Prevent deletion down below.
2017-03-16 20:29:47 -03:00
ref [ p ] = d . lastRefresh [ p ]
2015-05-13 12:03:31 +02:00
continue
}
2016-11-18 10:55:29 +01:00
select {
case ch <- tgroups :
case <- ctx . Done ( ) :
return
}
2016-02-18 17:26:27 +01:00
2015-05-13 12:03:31 +02:00
ref [ p ] = len ( tgroups )
}
// Send empty updates for sources that disappeared.
2017-03-16 20:29:47 -03:00
for f , n := range d . lastRefresh {
2015-05-13 12:03:31 +02:00
m , ok := ref [ f ]
if ! ok || n > m {
2024-09-09 21:41:53 -04:00
d . logger . Debug ( "file_sd refresh found file that should be removed" , "file" , f )
2017-11-30 23:54:40 -08:00
d . deleteTimestamp ( f )
2015-05-13 12:03:31 +02:00
for i := m ; i < n ; i ++ {
2016-11-18 10:55:29 +01:00
select {
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
case ch <- [ ] * targetgroup . Group { { Source : fileSource ( f , i ) } } :
2016-11-18 10:55:29 +01:00
case <- ctx . Done ( ) :
return
2016-02-18 17:26:27 +01:00
}
2015-05-13 12:03:31 +02:00
}
}
}
2017-03-16 20:29:47 -03:00
d . lastRefresh = ref
2015-05-13 12:03:31 +02:00
2017-03-16 20:29:47 -03:00
d . watchFiles ( )
2015-05-13 12:03:31 +02:00
}
// readFile reads a JSON or YAML list of targets groups from the file, depending on its
// file extension. It returns full configuration target groups.
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
func ( d * Discovery ) readFile ( filename string ) ( [ ] * targetgroup . Group , error ) {
2017-11-30 23:54:40 -08:00
fd , err := os . Open ( filename )
if err != nil {
return nil , err
}
defer fd . Close ( )
2022-04-27 11:24:36 +02:00
content , err := io . ReadAll ( fd )
2017-11-30 23:54:40 -08:00
if err != nil {
return nil , err
}
info , err := fd . Stat ( )
2015-05-13 12:03:31 +02:00
if err != nil {
return nil , err
}
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-30 01:31:34 +05:30
var targetGroups [ ] * targetgroup . Group
2015-05-13 12:03:31 +02:00
switch ext := filepath . Ext ( filename ) ; strings . ToLower ( ext ) {
case ".json" :
if err := json . Unmarshal ( content , & targetGroups ) ; err != nil {
return nil , err
}
case ".yml" , ".yaml" :
2018-04-04 11:07:39 +03:00
if err := yaml . UnmarshalStrict ( content , & targetGroups ) ; err != nil {
2015-05-13 12:03:31 +02:00
return nil , err
}
default :
2022-06-03 13:47:14 +02:00
panic ( fmt . Errorf ( "discovery.File.readFile: unhandled file extension %q" , ext ) )
2015-05-13 12:03:31 +02:00
}
for i , tg := range targetGroups {
2017-08-15 11:10:19 +08:00
if tg == nil {
err = errors . New ( "nil target group item found" )
return nil , err
}
2015-05-13 12:03:31 +02:00
tg . Source = fileSource ( filename , i )
2015-05-27 18:32:21 +02:00
if tg . Labels == nil {
2015-08-20 17:18:46 +02:00
tg . Labels = model . LabelSet { }
2015-05-19 15:24:26 +02:00
}
2015-08-24 15:07:27 +02:00
tg . Labels [ fileSDFilepathLabel ] = model . LabelValue ( filename )
2015-05-13 12:03:31 +02:00
}
2017-11-30 23:54:40 -08:00
d . writeTimestamp ( filename , float64 ( info . ModTime ( ) . Unix ( ) ) )
2015-05-13 12:03:31 +02:00
return targetGroups , nil
}
2017-11-30 23:54:40 -08:00
// fileSource returns a source ID for the i-th target group in the file.
func fileSource ( filename string , i int ) string {
return fmt . Sprintf ( "%s:%d" , filename , i )
}