mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-10 15:14:09 +00:00
lib/promscrape/discovery/kubernetes/: unify apiConfig creation
This commit is contained in:
parent
54414fefef
commit
c50fd219dc
7 changed files with 101 additions and 95 deletions
|
@ -301,12 +301,7 @@ type scrapeWorkConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendKubernetesScrapeWork(dst []ScrapeWork, sdc *kubernetes.SDConfig, baseDir string, swc *scrapeWorkConfig) []ScrapeWork {
|
func appendKubernetesScrapeWork(dst []ScrapeWork, sdc *kubernetes.SDConfig, baseDir string, swc *scrapeWorkConfig) []ScrapeWork {
|
||||||
ac, err := promauth.NewConfig(baseDir, sdc.BasicAuth, sdc.BearerToken, sdc.BearerTokenFile, sdc.TLSConfig)
|
targetLabels, err := kubernetes.GetLabels(sdc, baseDir)
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("cannot parse auth config for `kubernetes_sd_config` for `job_name` %q: %s; skipping it", swc.jobName, err)
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
targetLabels, err := kubernetes.GetLabels(ac, sdc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("error when discovering kubernetes nodes for `job_name` %q: %s; skipping it", swc.jobName, err)
|
logger.Errorf("error when discovering kubernetes nodes for `job_name` %q: %s; skipping it", swc.jobName, err)
|
||||||
return dst
|
return dst
|
||||||
|
|
|
@ -16,34 +16,101 @@ import (
|
||||||
|
|
||||||
// apiConfig contains config for API server
|
// apiConfig contains config for API server
|
||||||
type apiConfig struct {
|
type apiConfig struct {
|
||||||
Server string
|
client *fasthttp.HostClient
|
||||||
AuthConfig *promauth.Config
|
server string
|
||||||
Namespaces []string
|
hostPort string
|
||||||
Selectors []Selector
|
authConfig *promauth.Config
|
||||||
|
namespaces []string
|
||||||
|
selectors []Selector
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAPIResponse(cfg *apiConfig, role, path string) ([]byte, error) {
|
func getAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
|
||||||
hcv, err := getHostClient(cfg.Server, cfg.AuthConfig)
|
apiConfigMapLock.Lock()
|
||||||
|
defer apiConfigMapLock.Unlock()
|
||||||
|
|
||||||
|
if !hasAPIConfigMapCleaner {
|
||||||
|
hasAPIConfigMapCleaner = true
|
||||||
|
go apiConfigMapCleaner()
|
||||||
|
}
|
||||||
|
|
||||||
|
e := apiConfigMap[sdc]
|
||||||
|
if e != nil {
|
||||||
|
e.lastAccessTime = time.Now()
|
||||||
|
return e.cfg, nil
|
||||||
|
}
|
||||||
|
cfg, err := newAPIConfig(sdc, baseDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
query := joinSelectors(role, cfg.Namespaces, cfg.Selectors)
|
apiConfigMap[sdc] = &apiConfigMapEntry{
|
||||||
|
cfg: cfg,
|
||||||
|
lastAccessTime: time.Now(),
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiConfigMapCleaner() {
|
||||||
|
tc := time.NewTicker(15 * time.Minute)
|
||||||
|
for currentTime := range tc.C {
|
||||||
|
apiConfigMapLock.Lock()
|
||||||
|
for k, e := range apiConfigMap {
|
||||||
|
if currentTime.Sub(e.lastAccessTime) > 10*time.Minute {
|
||||||
|
delete(apiConfigMap, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apiConfigMapLock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiConfigMapEntry struct {
|
||||||
|
cfg *apiConfig
|
||||||
|
lastAccessTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
apiConfigMap = make(map[*SDConfig]*apiConfigMapEntry)
|
||||||
|
apiConfigMapLock sync.Mutex
|
||||||
|
hasAPIConfigMapCleaner bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
|
||||||
|
ac, err := promauth.NewConfig(baseDir, sdc.BasicAuth, sdc.BearerToken, sdc.BearerTokenFile, sdc.TLSConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot parse auth config: %s", err)
|
||||||
|
}
|
||||||
|
hcv, err := newHostClient(sdc.APIServer, ac)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot create HTTP client for %q: %s", sdc.APIServer, err)
|
||||||
|
}
|
||||||
|
cfg := &apiConfig{
|
||||||
|
client: hcv.hc,
|
||||||
|
server: hcv.apiServer,
|
||||||
|
hostPort: hcv.hostPort,
|
||||||
|
authConfig: hcv.ac,
|
||||||
|
namespaces: sdc.Namespaces.Names,
|
||||||
|
selectors: sdc.Selectors,
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAPIResponse(cfg *apiConfig, role, path string) ([]byte, error) {
|
||||||
|
query := joinSelectors(role, cfg.namespaces, cfg.selectors)
|
||||||
if len(query) > 0 {
|
if len(query) > 0 {
|
||||||
path += "?" + query
|
path += "?" + query
|
||||||
}
|
}
|
||||||
requestURL := hcv.apiServer + path
|
requestURL := cfg.server + path
|
||||||
var u fasthttp.URI
|
var u fasthttp.URI
|
||||||
u.Update(requestURL)
|
u.Update(requestURL)
|
||||||
var req fasthttp.Request
|
var req fasthttp.Request
|
||||||
req.SetRequestURIBytes(u.RequestURI())
|
req.SetRequestURIBytes(u.RequestURI())
|
||||||
req.SetHost(hcv.hostPort)
|
req.SetHost(cfg.hostPort)
|
||||||
req.Header.Set("Accept-Encoding", "gzip")
|
req.Header.Set("Accept-Encoding", "gzip")
|
||||||
if hcv.ac != nil && hcv.ac.Authorization != "" {
|
if cfg.authConfig != nil && cfg.authConfig.Authorization != "" {
|
||||||
req.Header.Set("Authorization", hcv.ac.Authorization)
|
req.Header.Set("Authorization", cfg.authConfig.Authorization)
|
||||||
}
|
}
|
||||||
var resp fasthttp.Response
|
var resp fasthttp.Response
|
||||||
// There is no need in calling DoTimeout, since the timeout is already set in hc.ReadTimeout above.
|
// There is no need in calling DoTimeout, since the timeout is already set in hc.ReadTimeout above.
|
||||||
if err := hcv.hc.Do(&req, &resp); err != nil {
|
if err := cfg.client.Do(&req, &resp); err != nil {
|
||||||
return nil, fmt.Errorf("cannot fetch %q: %s", requestURL, err)
|
return nil, fmt.Errorf("cannot fetch %q: %s", requestURL, err)
|
||||||
}
|
}
|
||||||
var data []byte
|
var data []byte
|
||||||
|
@ -64,67 +131,13 @@ func getAPIResponse(cfg *apiConfig, role, path string) ([]byte, error) {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHostClient(apiServer string, ac *promauth.Config) (*hcValue, error) {
|
|
||||||
if len(apiServer) == 0 {
|
|
||||||
// ac is ignored when apiServer should be auto-discovered when running inside k8s pod.
|
|
||||||
ac = nil
|
|
||||||
}
|
|
||||||
k := hcKey{
|
|
||||||
apiServer: apiServer,
|
|
||||||
ac: ac,
|
|
||||||
}
|
|
||||||
hcMapLock.Lock()
|
|
||||||
defer hcMapLock.Unlock()
|
|
||||||
|
|
||||||
if !hasHCMapCleaner {
|
|
||||||
go hcMapCleaner()
|
|
||||||
hasHCMapCleaner = true
|
|
||||||
}
|
|
||||||
hcv := hcMap[k]
|
|
||||||
if hcv == nil {
|
|
||||||
hcvNew, err := newHostClient(apiServer, ac)
|
|
||||||
if err != nil {
|
|
||||||
return hcv, fmt.Errorf("cannot create new HTTP client for %q: %s", apiServer, err)
|
|
||||||
}
|
|
||||||
hcMap[k] = hcvNew
|
|
||||||
hcv = hcvNew
|
|
||||||
}
|
|
||||||
hcv.lastAccessTime = time.Now()
|
|
||||||
return hcv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func hcMapCleaner() {
|
|
||||||
tc := time.NewTicker(15 * time.Minute)
|
|
||||||
for currentTime := range tc.C {
|
|
||||||
hcMapLock.Lock()
|
|
||||||
for k, v := range hcMap {
|
|
||||||
if currentTime.Sub(v.lastAccessTime) > 10*time.Minute {
|
|
||||||
delete(hcMap, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hcMapLock.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type hcKey struct {
|
|
||||||
apiServer string
|
|
||||||
ac *promauth.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
type hcValue struct {
|
type hcValue struct {
|
||||||
hc *fasthttp.HostClient
|
hc *fasthttp.HostClient
|
||||||
ac *promauth.Config
|
ac *promauth.Config
|
||||||
apiServer string
|
apiServer string
|
||||||
hostPort string
|
hostPort string
|
||||||
lastAccessTime time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
hasHCMapCleaner bool
|
|
||||||
hcMap = make(map[hcKey]*hcValue)
|
|
||||||
hcMapLock sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
func newHostClient(apiServer string, ac *promauth.Config) (*hcValue, error) {
|
func newHostClient(apiServer string, ac *promauth.Config) (*hcValue, error) {
|
||||||
if len(apiServer) == 0 {
|
if len(apiServer) == 0 {
|
||||||
// Assume we run at k8s pod.
|
// Assume we run at k8s pod.
|
||||||
|
|
|
@ -29,14 +29,14 @@ func getEndpointsLabels(cfg *apiConfig) ([]map[string]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEndpoints(cfg *apiConfig) ([]Endpoints, error) {
|
func getEndpoints(cfg *apiConfig) ([]Endpoints, error) {
|
||||||
if len(cfg.Namespaces) == 0 {
|
if len(cfg.namespaces) == 0 {
|
||||||
return getEndpointsByPath(cfg, "/api/v1/endpoints")
|
return getEndpointsByPath(cfg, "/api/v1/endpoints")
|
||||||
}
|
}
|
||||||
// Query /api/v1/namespaces/* for each namespace.
|
// Query /api/v1/namespaces/* for each namespace.
|
||||||
// This fixes authorization issue at https://github.com/VictoriaMetrics/VictoriaMetrics/issues/432
|
// This fixes authorization issue at https://github.com/VictoriaMetrics/VictoriaMetrics/issues/432
|
||||||
cfgCopy := *cfg
|
cfgCopy := *cfg
|
||||||
namespaces := cfgCopy.Namespaces
|
namespaces := cfgCopy.namespaces
|
||||||
cfgCopy.Namespaces = nil
|
cfgCopy.namespaces = nil
|
||||||
cfg = &cfgCopy
|
cfg = &cfgCopy
|
||||||
var result []Endpoints
|
var result []Endpoints
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
|
|
|
@ -19,14 +19,14 @@ func getIngressesLabels(cfg *apiConfig) ([]map[string]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIngresses(cfg *apiConfig) ([]Ingress, error) {
|
func getIngresses(cfg *apiConfig) ([]Ingress, error) {
|
||||||
if len(cfg.Namespaces) == 0 {
|
if len(cfg.namespaces) == 0 {
|
||||||
return getIngressesByPath(cfg, "/apis/extensions/v1beta1/ingresses")
|
return getIngressesByPath(cfg, "/apis/extensions/v1beta1/ingresses")
|
||||||
}
|
}
|
||||||
// Query /api/v1/namespaces/* for each namespace.
|
// Query /api/v1/namespaces/* for each namespace.
|
||||||
// This fixes authorization issue at https://github.com/VictoriaMetrics/VictoriaMetrics/issues/432
|
// This fixes authorization issue at https://github.com/VictoriaMetrics/VictoriaMetrics/issues/432
|
||||||
cfgCopy := *cfg
|
cfgCopy := *cfg
|
||||||
namespaces := cfgCopy.Namespaces
|
namespaces := cfgCopy.namespaces
|
||||||
cfgCopy.Namespaces = nil
|
cfgCopy.namespaces = nil
|
||||||
cfg = &cfgCopy
|
cfg = &cfgCopy
|
||||||
var result []Ingress
|
var result []Ingress
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
|
|
|
@ -35,13 +35,11 @@ type Selector struct {
|
||||||
Field string `yaml:"field"`
|
Field string `yaml:"field"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabels returns labels for the given k8s role and the given ac and sdc.
|
// GetLabels returns labels for the given sdc and baseDir.
|
||||||
func GetLabels(ac *promauth.Config, sdc *SDConfig) ([]map[string]string, error) {
|
func GetLabels(sdc *SDConfig, baseDir string) ([]map[string]string, error) {
|
||||||
cfg := &apiConfig{
|
cfg, err := getAPIConfig(sdc, baseDir)
|
||||||
Server: sdc.APIServer,
|
if err != nil {
|
||||||
AuthConfig: ac,
|
return nil, fmt.Errorf("cannot create API config: %s", err)
|
||||||
Namespaces: sdc.Namespaces.Names,
|
|
||||||
Selectors: sdc.Selectors,
|
|
||||||
}
|
}
|
||||||
switch sdc.Role {
|
switch sdc.Role {
|
||||||
case "node":
|
case "node":
|
||||||
|
|
|
@ -23,14 +23,14 @@ func getPodsLabels(cfg *apiConfig) ([]map[string]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPods(cfg *apiConfig) ([]Pod, error) {
|
func getPods(cfg *apiConfig) ([]Pod, error) {
|
||||||
if len(cfg.Namespaces) == 0 {
|
if len(cfg.namespaces) == 0 {
|
||||||
return getPodsByPath(cfg, "/api/v1/pods")
|
return getPodsByPath(cfg, "/api/v1/pods")
|
||||||
}
|
}
|
||||||
// Query /api/v1/namespaces/* for each namespace.
|
// Query /api/v1/namespaces/* for each namespace.
|
||||||
// This fixes authorization issue at https://github.com/VictoriaMetrics/VictoriaMetrics/issues/432
|
// This fixes authorization issue at https://github.com/VictoriaMetrics/VictoriaMetrics/issues/432
|
||||||
cfgCopy := *cfg
|
cfgCopy := *cfg
|
||||||
namespaces := cfgCopy.Namespaces
|
namespaces := cfgCopy.namespaces
|
||||||
cfgCopy.Namespaces = nil
|
cfgCopy.namespaces = nil
|
||||||
cfg = &cfgCopy
|
cfg = &cfgCopy
|
||||||
var result []Pod
|
var result []Pod
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
|
|
|
@ -21,14 +21,14 @@ func getServicesLabels(cfg *apiConfig) ([]map[string]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServices(cfg *apiConfig) ([]Service, error) {
|
func getServices(cfg *apiConfig) ([]Service, error) {
|
||||||
if len(cfg.Namespaces) == 0 {
|
if len(cfg.namespaces) == 0 {
|
||||||
return getServicesByPath(cfg, "/api/v1/services")
|
return getServicesByPath(cfg, "/api/v1/services")
|
||||||
}
|
}
|
||||||
// Query /api/v1/namespaces/* for each namespace.
|
// Query /api/v1/namespaces/* for each namespace.
|
||||||
// This fixes authorization issue at https://github.com/VictoriaMetrics/VictoriaMetrics/issues/432
|
// This fixes authorization issue at https://github.com/VictoriaMetrics/VictoriaMetrics/issues/432
|
||||||
cfgCopy := *cfg
|
cfgCopy := *cfg
|
||||||
namespaces := cfgCopy.Namespaces
|
namespaces := cfgCopy.namespaces
|
||||||
cfgCopy.Namespaces = nil
|
cfgCopy.namespaces = nil
|
||||||
cfg = &cfgCopy
|
cfg = &cfgCopy
|
||||||
var result []Service
|
var result []Service
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
|
|
Loading…
Reference in a new issue