diff --git a/Gopkg.lock b/Gopkg.lock index 318c790b..1f45f921 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -279,20 +279,22 @@ version = "v1.1.5" [[projects]] - digest = "1:46bdda4b93f3a1169365a10a9eef6cfba14fbb59a636f8562c6eba578afb48b8" + digest = "1:cf71ca07e2ee5914c776d9eaec4a7f63f4ddb013d55e39ffd5689fe4cd2df861" name = "github.com/kubernetes-incubator/custom-metrics-apiserver" packages = [ "pkg/apiserver", "pkg/apiserver/installer", + "pkg/cmd", "pkg/cmd/server", "pkg/dynamicmapper", "pkg/provider", + "pkg/provider/helpers", "pkg/registry/custom_metrics", "pkg/registry/external_metrics", ] pruneopts = "UT" - revision = "d8f23423aa1d0ff2bc9656da863d721725b3c68a" - version = "kubernetes-1.11.0-rc.1" + revision = "f54b0d6f31d8e0f0a2d7be372ddd837a2ef15d97" + version = "kubernetes-1.11.2" [[projects]] branch = "master" @@ -937,9 +939,9 @@ analyzer-version = 1 input-imports = [ "github.com/golang/glog", - "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server", - "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/dynamicmapper", + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd", "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider", + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/helpers", "github.com/prometheus/client_golang/prometheus", "github.com/prometheus/common/model", "github.com/spf13/cobra", @@ -952,14 +954,12 @@ "k8s.io/apimachinery/pkg/api/meta", "k8s.io/apimachinery/pkg/api/resource", "k8s.io/apimachinery/pkg/apis/meta/v1", - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", "k8s.io/apimachinery/pkg/labels", - "k8s.io/apimachinery/pkg/runtime", "k8s.io/apimachinery/pkg/runtime/schema", + "k8s.io/apimachinery/pkg/types", "k8s.io/apimachinery/pkg/util/runtime", "k8s.io/apimachinery/pkg/util/wait", "k8s.io/apiserver/pkg/util/logs", - "k8s.io/client-go/discovery", "k8s.io/client-go/dynamic", "k8s.io/client-go/dynamic/fake", "k8s.io/client-go/rest", diff --git a/Gopkg.toml b/Gopkg.toml index a6c7c162..e118a149 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -48,8 +48,8 @@ # Kubernetes incubator deps [[constraint]] - version = "kubernetes-1.11.0-rc.1" name = "github.com/kubernetes-incubator/custom-metrics-apiserver" + version = "kubernetes-1.11.2" # Core Kubernetes deps [[constraint]] @@ -72,6 +72,11 @@ name = "k8s.io/metrics" version = "kubernetes-1.11.3" +# messed up kubernetes dep +[[override]] + name = "github.com/json-iterator/go" + version = "1.1.5" + # Test deps [[constraint]] name = "github.com/stretchr/testify" diff --git a/cmd/adapter/adapter.go b/cmd/adapter/adapter.go index 8cfad818..abc0338b 100644 --- a/cmd/adapter/adapter.go +++ b/cmd/adapter/adapter.go @@ -18,26 +18,165 @@ package main import ( "flag" + "fmt" + "net/http" + "net/url" "os" - "runtime" + "time" + "github.com/golang/glog" + basecmd "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd" + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/util/logs" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" - "github.com/directxman12/k8s-prometheus-adapter/cmd/adapter/app" + prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" + mprom "github.com/directxman12/k8s-prometheus-adapter/pkg/client/metrics" + adaptercfg "github.com/directxman12/k8s-prometheus-adapter/pkg/config" + cmprov "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider" ) +type PrometheusAdapter struct { + basecmd.AdapterBase + + // PrometheusURL is the URL describing how to connect to Prometheus. Query parameters configure connection options. + PrometheusURL string + // PrometheusAuthInCluster enables using the auth details from the in-cluster kubeconfig to connect to Prometheus + PrometheusAuthInCluster bool + // PrometheusAuthConf is the kubeconfig file that contains auth details used to connect to Prometheus + PrometheusAuthConf string + // AdapterConfigFile points to the file containing the metrics discovery configuration. + AdapterConfigFile string + // MetricsRelistInterval is the interval at which to relist the set of available metrics + MetricsRelistInterval time.Duration +} + +func (cmd *PrometheusAdapter) makePromClient() (prom.Client, error) { + baseURL, err := url.Parse(cmd.PrometheusURL) + if err != nil { + return nil, fmt.Errorf("invalid Prometheus URL %q: %v", baseURL, err) + } + promHTTPClient, err := makeHTTPClient(cmd.PrometheusAuthInCluster, cmd.PrometheusAuthConf) + if err != nil { + return nil, err + } + genericPromClient := prom.NewGenericAPIClient(promHTTPClient, baseURL) + instrumentedGenericPromClient := mprom.InstrumentGenericAPIClient(genericPromClient, baseURL.String()) + return prom.NewClientForAPI(instrumentedGenericPromClient), nil +} + +func (cmd *PrometheusAdapter) addFlags() { + cmd.Flags().StringVar(&cmd.PrometheusURL, "prometheus-url", cmd.PrometheusURL, + "URL for connecting to Prometheus.") + cmd.Flags().BoolVar(&cmd.PrometheusAuthInCluster, "prometheus-auth-incluster", cmd.PrometheusAuthInCluster, + "use auth details from the in-cluster kubeconfig when connecting to prometheus.") + cmd.Flags().StringVar(&cmd.PrometheusAuthConf, "prometheus-auth-config", cmd.PrometheusAuthConf, + "kubeconfig file used to configure auth when connecting to Prometheus.") + cmd.Flags().StringVar(&cmd.AdapterConfigFile, "config", cmd.AdapterConfigFile, + "Configuration file containing details of how to transform between Prometheus metrics "+ + "and custom metrics API resources") + cmd.Flags().DurationVar(&cmd.MetricsRelistInterval, "metrics-relist-interval", cmd.MetricsRelistInterval, ""+ + "interval at which to re-list the set of all available metrics from Prometheus") +} + +func (cmd *PrometheusAdapter) makeProvider(stopCh <-chan struct{}) (provider.CustomMetricsProvider, error) { + // load metrics discovery configuration + if cmd.AdapterConfigFile == "" { + return nil, fmt.Errorf("no metrics discovery configuration file specified (make sure to use --config)") + } + metricsConfig, err := adaptercfg.FromFile(cmd.AdapterConfigFile) + if err != nil { + return nil, fmt.Errorf("unable to load metrics discovery configuration: %v", err) + } + + // make the prometheus client + promClient, err := cmd.makePromClient() + if err != nil { + return nil, fmt.Errorf("unable to construct Prometheus client: %v", err) + } + + // grab the mapper and dynamic client + mapper, err := cmd.RESTMapper() + if err != nil { + return nil, fmt.Errorf("unable to construct RESTMapper: %v", err) + } + dynClient, err := cmd.DynamicClient() + if err != nil { + return nil, fmt.Errorf("unable to construct Kubernetes client: %v", err) + } + + // extract the namers + namers, err := cmprov.NamersFromConfig(metricsConfig, mapper) + if err != nil { + return nil, fmt.Errorf("unable to construct naming scheme from metrics rules: %v", err) + } + + // construct the provider and start it + cmProvider, runner := cmprov.NewPrometheusProvider(mapper, dynClient, promClient, namers, cmd.MetricsRelistInterval) + runner.RunUntil(stopCh) + + return cmProvider, nil +} + func main() { logs.InitLogs() defer logs.FlushLogs() - if len(os.Getenv("GOMAXPROCS")) == 0 { - runtime.GOMAXPROCS(runtime.NumCPU()) + // set up flags + cmd := &PrometheusAdapter{ + PrometheusURL: "https://localhost", + MetricsRelistInterval: 10 * time.Minute, + } + cmd.addFlags() + cmd.Flags().AddGoFlagSet(flag.CommandLine) // make sure we get the glog flags + cmd.Flags().Parse(os.Args) + + // construct the provider + cmProvider, err := cmd.makeProvider(wait.NeverStop) + if err != nil { + glog.Fatalf("unable to construct custom metrics provider: %v", err) } - cmd := app.NewCommandStartPrometheusAdapterServer(os.Stdout, os.Stderr, wait.NeverStop) - cmd.Flags().AddGoFlagSet(flag.CommandLine) - if err := cmd.Execute(); err != nil { - panic(err) + // attach the provider to the server and run it + cmd.WithCustomMetrics(cmProvider) + if err := cmd.Run(wait.NeverStop); err != nil { + glog.Fatalf("unable to run custom metrics adapter: %v", err) } } + +// makeHTTPClient constructs an HTTP for connecting with the given auth options. +func makeHTTPClient(inClusterAuth bool, kubeConfigPath string) (*http.Client, error) { + // make sure we're not trying to use two different sources of auth + if inClusterAuth && kubeConfigPath != "" { + return nil, fmt.Errorf("may not use both in-cluster auth and an explicit kubeconfig at the same time") + } + + // return the default client if we're using no auth + if !inClusterAuth && kubeConfigPath == "" { + return http.DefaultClient, nil + } + + var authConf *rest.Config + if kubeConfigPath != "" { + var err error + loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfigPath} + loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) + authConf, err = loader.ClientConfig() + if err != nil { + return nil, fmt.Errorf("unable to construct auth configuration from %q for connecting to Prometheus: %v", kubeConfigPath, err) + } + } else { + var err error + authConf, err = rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("unable to construct in-cluster auth configuration for connecting to Prometheus: %v", err) + } + } + tr, err := rest.TransportFor(authConf) + if err != nil { + return nil, fmt.Errorf("unable to construct client transport for connecting to Prometheus: %v", err) + } + return &http.Client{Transport: tr}, nil +} diff --git a/cmd/adapter/app/start.go b/cmd/adapter/app/start.go deleted file mode 100644 index 9859365d..00000000 --- a/cmd/adapter/app/start.go +++ /dev/null @@ -1,219 +0,0 @@ -/* -Copyright 2017 The Kubernetes 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. -*/ - -package app - -import ( - "fmt" - "io" - "net/http" - "net/url" - "time" - - "github.com/spf13/cobra" - "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - - prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" - mprom "github.com/directxman12/k8s-prometheus-adapter/pkg/client/metrics" - adaptercfg "github.com/directxman12/k8s-prometheus-adapter/pkg/config" - cmprov "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider" - "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server" - "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/dynamicmapper" -) - -// NewCommandStartPrometheusAdapterServer provides a CLI handler for 'start master' command -func NewCommandStartPrometheusAdapterServer(out, errOut io.Writer, stopCh <-chan struct{}) *cobra.Command { - baseOpts := server.NewCustomMetricsAdapterServerOptions(out, errOut) - o := PrometheusAdapterServerOptions{ - CustomMetricsAdapterServerOptions: baseOpts, - MetricsRelistInterval: 10 * time.Minute, - PrometheusURL: "https://localhost", - } - - cmd := &cobra.Command{ - Short: "Launch the custom metrics API adapter server", - Long: "Launch the custom metrics API adapter server", - RunE: func(c *cobra.Command, args []string) error { - if err := o.Complete(); err != nil { - return err - } - if err := o.Validate(args); err != nil { - return err - } - if err := o.RunCustomMetricsAdapterServer(stopCh); err != nil { - return err - } - return nil - }, - } - - flags := cmd.Flags() - o.SecureServing.AddFlags(flags) - o.Authentication.AddFlags(flags) - o.Authorization.AddFlags(flags) - o.Features.AddFlags(flags) - - flags.StringVar(&o.RemoteKubeConfigFile, "lister-kubeconfig", o.RemoteKubeConfigFile, ""+ - "kubeconfig file pointing at the 'core' kubernetes server with enough rights to list "+ - "any described objets") - flags.DurationVar(&o.MetricsRelistInterval, "metrics-relist-interval", o.MetricsRelistInterval, ""+ - "interval at which to re-list the set of all available metrics from Prometheus") - flags.DurationVar(&o.DiscoveryInterval, "discovery-interval", o.DiscoveryInterval, ""+ - "interval at which to refresh API discovery information") - flags.StringVar(&o.PrometheusURL, "prometheus-url", o.PrometheusURL, - "URL for connecting to Prometheus.") - flags.BoolVar(&o.PrometheusAuthInCluster, "prometheus-auth-incluster", o.PrometheusAuthInCluster, - "use auth details from the in-cluster kubeconfig when connecting to prometheus.") - flags.StringVar(&o.PrometheusAuthConf, "prometheus-auth-config", o.PrometheusAuthConf, - "kubeconfig file used to configure auth when connecting to Prometheus.") - flags.StringVar(&o.AdapterConfigFile, "config", o.AdapterConfigFile, - "Configuration file containing details of how to transform between Prometheus metrics "+ - "and custom metrics API resources") - - cmd.MarkFlagRequired("config") - - return cmd -} - -// makeHTTPClient constructs an HTTP for connecting with the given auth options. -func makeHTTPClient(inClusterAuth bool, kubeConfigPath string) (*http.Client, error) { - // make sure we're not trying to use two different sources of auth - if inClusterAuth && kubeConfigPath != "" { - return nil, fmt.Errorf("may not use both in-cluster auth and an explicit kubeconfig at the same time") - } - - // return the default client if we're using no auth - if !inClusterAuth && kubeConfigPath == "" { - return http.DefaultClient, nil - } - - var authConf *rest.Config - if kubeConfigPath != "" { - var err error - loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfigPath} - loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) - authConf, err = loader.ClientConfig() - if err != nil { - return nil, fmt.Errorf("unable to construct auth configuration from %q for connecting to Prometheus: %v", kubeConfigPath, err) - } - } else { - var err error - authConf, err = rest.InClusterConfig() - if err != nil { - return nil, fmt.Errorf("unable to construct in-cluster auth configuration for connecting to Prometheus: %v", err) - } - } - tr, err := rest.TransportFor(authConf) - if err != nil { - return nil, fmt.Errorf("unable to construct client transport for connecting to Prometheus: %v", err) - } - return &http.Client{Transport: tr}, nil -} - -func (o PrometheusAdapterServerOptions) RunCustomMetricsAdapterServer(stopCh <-chan struct{}) error { - if o.AdapterConfigFile == "" { - return fmt.Errorf("no discovery configuration file specified") - } - - metricsConfig, err := adaptercfg.FromFile(o.AdapterConfigFile) - if err != nil { - return fmt.Errorf("unable to load metrics discovery configuration: %v", err) - } - - config, err := o.Config() - if err != nil { - return err - } - - config.GenericConfig.EnableMetrics = true - - var clientConfig *rest.Config - if len(o.RemoteKubeConfigFile) > 0 { - loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: o.RemoteKubeConfigFile} - loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) - - clientConfig, err = loader.ClientConfig() - } else { - clientConfig, err = rest.InClusterConfig() - } - if err != nil { - return fmt.Errorf("unable to construct lister client config to initialize provider: %v", err) - } - - discoveryClient, err := discovery.NewDiscoveryClientForConfig(clientConfig) - if err != nil { - return fmt.Errorf("unable to construct discovery client for dynamic client: %v", err) - } - - dynamicMapper, err := dynamicmapper.NewRESTMapper(discoveryClient, o.DiscoveryInterval) - if err != nil { - return fmt.Errorf("unable to construct dynamic discovery mapper: %v", err) - } - - dynamicClient, err := dynamic.NewForConfig(clientConfig) - if err != nil { - return fmt.Errorf("unable to construct lister client to initialize provider: %v", err) - } - - // TODO: actually configure this client (strip query vars, etc) - baseURL, err := url.Parse(o.PrometheusURL) - if err != nil { - return fmt.Errorf("invalid Prometheus URL %q: %v", baseURL, err) - } - promHTTPClient, err := makeHTTPClient(o.PrometheusAuthInCluster, o.PrometheusAuthConf) - if err != nil { - return err - } - genericPromClient := prom.NewGenericAPIClient(promHTTPClient, baseURL) - instrumentedGenericPromClient := mprom.InstrumentGenericAPIClient(genericPromClient, baseURL.String()) - promClient := prom.NewClientForAPI(instrumentedGenericPromClient) - - namers, err := cmprov.NamersFromConfig(metricsConfig, dynamicMapper) - if err != nil { - return fmt.Errorf("unable to construct naming scheme from metrics rules: %v", err) - } - - cmProvider, runner := cmprov.NewPrometheusProvider(dynamicMapper, dynamicClient, promClient, namers, o.MetricsRelistInterval) - runner.RunUntil(stopCh) - - server, err := config.Complete().New("prometheus-custom-metrics-adapter", cmProvider, nil) - if err != nil { - return err - } - return server.GenericAPIServer.PrepareRun().Run(stopCh) -} - -type PrometheusAdapterServerOptions struct { - *server.CustomMetricsAdapterServerOptions - - // RemoteKubeConfigFile is the config used to list pods from the master API server - RemoteKubeConfigFile string - // MetricsRelistInterval is the interval at which to relist the set of available metrics - MetricsRelistInterval time.Duration - // DiscoveryInterval is the interval at which discovery information is refreshed - DiscoveryInterval time.Duration - // PrometheusURL is the URL describing how to connect to Prometheus. Query parameters configure connection options. - PrometheusURL string - // PrometheusAuthInCluster enables using the auth details from the in-cluster kubeconfig to connect to Prometheus - PrometheusAuthInCluster bool - // PrometheusAuthConf is the kubeconfig file that contains auth details used to connect to Prometheus - PrometheusAuthConf string - // AdapterConfigFile points to the file containing the metrics discovery configuration. - AdapterConfigFile string -} diff --git a/pkg/custom-provider/provider.go b/pkg/custom-provider/provider.go index b8e122cf..96f8831c 100644 --- a/pkg/custom-provider/provider.go +++ b/pkg/custom-provider/provider.go @@ -23,15 +23,14 @@ import ( "github.com/golang/glog" "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider" + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/helpers" pmodel "github.com/prometheus/common/model" apierr "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/dynamic" @@ -76,52 +75,38 @@ func NewPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.Interfa }, lister } -func (p *prometheusProvider) metricFor(value pmodel.SampleValue, groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error) { - kind, err := p.mapper.KindFor(groupResource.WithVersion("")) +func (p *prometheusProvider) metricFor(value pmodel.SampleValue, name types.NamespacedName, info provider.CustomMetricInfo) (*custom_metrics.MetricValue, error) { + ref, err := helpers.ReferenceFor(p.mapper, name, info) if err != nil { return nil, err } return &custom_metrics.MetricValue{ - DescribedObject: custom_metrics.ObjectReference{ - APIVersion: groupResource.Group + "/" + runtime.APIVersionInternal, - Kind: kind.Kind, - Name: name, - Namespace: namespace, - }, - MetricName: metricName, - Timestamp: metav1.Time{time.Now()}, - Value: *resource.NewMilliQuantity(int64(value*1000.0), resource.DecimalSI), + DescribedObject: ref, + MetricName: info.Metric, + // TODO(directxman12): use the right timestamp + Timestamp: metav1.Time{time.Now()}, + Value: *resource.NewMilliQuantity(int64(value*1000.0), resource.DecimalSI), }, nil } -func (p *prometheusProvider) metricsFor(valueSet pmodel.Vector, info provider.CustomMetricInfo, list runtime.Object) (*custom_metrics.MetricValueList, error) { - if !apimeta.IsListType(list) { - return nil, apierr.NewInternalError(fmt.Errorf("result of label selector list operation was not a list")) - } - +func (p *prometheusProvider) metricsFor(valueSet pmodel.Vector, info provider.CustomMetricInfo, namespace string, names []string) (*custom_metrics.MetricValueList, error) { values, found := p.MatchValuesToNames(info, valueSet) if !found { return nil, provider.NewMetricNotFoundError(info.GroupResource, info.Metric) } res := []custom_metrics.MetricValue{} - err := apimeta.EachListItem(list, func(item runtime.Object) error { - objUnstructured := item.(*unstructured.Unstructured) - objName := objUnstructured.GetName() - if _, found := values[objName]; !found { - return nil + for _, name := range names { + if _, found := values[name]; !found { + continue } - value, err := p.metricFor(values[objName], info.GroupResource, objUnstructured.GetNamespace(), objName, info.Metric) + + value, err := p.metricFor(values[name], types.NamespacedName{Namespace: namespace, Name: name}, info) if err != nil { - return err + return nil, err } res = append(res, *value) - - return nil - }) - if err != nil { - return nil, err } return &custom_metrics.MetricValueList{ @@ -151,14 +136,16 @@ func (p *prometheusProvider) buildQuery(info provider.CustomMetricInfo, namespac return *queryResults.Vector, nil } -func (p *prometheusProvider) getSingle(info provider.CustomMetricInfo, namespace, name string) (*custom_metrics.MetricValue, error) { - queryResults, err := p.buildQuery(info, namespace, name) +func (p *prometheusProvider) GetMetricByName(name types.NamespacedName, info provider.CustomMetricInfo) (*custom_metrics.MetricValue, error) { + // construct a query + queryResults, err := p.buildQuery(info, name.Namespace, name.Name) if err != nil { return nil, err } + // associate the metrics if len(queryResults) < 1 { - return nil, provider.NewMetricNotFoundForError(info.GroupResource, info.Metric, name) + return nil, provider.NewMetricNotFoundForError(info.GroupResource, info.Metric, name.Name) } namedValues, found := p.MatchValuesToNames(info, queryResults) @@ -170,97 +157,33 @@ func (p *prometheusProvider) getSingle(info provider.CustomMetricInfo, namespace glog.V(2).Infof("Got more than one result (%v results) when fetching metric %s for %q, using the first one with a matching name...", len(queryResults), info.String(), name) } - resultValue, nameFound := namedValues[name] + resultValue, nameFound := namedValues[name.Name] if !nameFound { glog.Errorf("None of the results returned by when fetching metric %s for %q matched the resource name", info.String(), name) - return nil, provider.NewMetricNotFoundForError(info.GroupResource, info.Metric, name) + return nil, provider.NewMetricNotFoundForError(info.GroupResource, info.Metric, name.Name) } - return p.metricFor(resultValue, info.GroupResource, "", name, info.Metric) + // return the resulting metric + return p.metricFor(resultValue, name, info) } -func (p *prometheusProvider) getMultiple(info provider.CustomMetricInfo, namespace string, selector labels.Selector) (*custom_metrics.MetricValueList, error) { - fullResources, err := p.mapper.ResourcesFor(info.GroupResource.WithVersion("")) - if err == nil && len(fullResources) == 0 { - err = fmt.Errorf("no fully versioned resources known for group-resource %v", info.GroupResource) - } - if err != nil { - glog.Errorf("unable to find preferred version to list matching resource names: %v", err) - // don't leak implementation details to the user - return nil, apierr.NewInternalError(fmt.Errorf("unable to list matching resources")) - } - var client dynamic.ResourceInterface - if namespace != "" { - client = p.kubeClient.Resource(fullResources[0]).Namespace(namespace) - } else { - client = p.kubeClient.Resource(fullResources[0]) - } - - // actually list the objects matching the label selector - matchingObjectsRaw, err := client.List(metav1.ListOptions{LabelSelector: selector.String()}) +func (p *prometheusProvider) GetMetricBySelector(namespace string, selector labels.Selector, info provider.CustomMetricInfo) (*custom_metrics.MetricValueList, error) { + // fetch a list of relevant resource names + resourceNames, err := helpers.ListObjectNames(p.mapper, p.kubeClient, namespace, selector, info) if err != nil { glog.Errorf("unable to list matching resource names: %v", err) // don't leak implementation details to the user return nil, apierr.NewInternalError(fmt.Errorf("unable to list matching resources")) } - // make sure we have a list - if !apimeta.IsListType(matchingObjectsRaw) { - return nil, apierr.NewInternalError(fmt.Errorf("result of label selector list operation was not a list")) - } - - // convert a list of objects into the corresponding list of names - resourceNames := []string{} - err = apimeta.EachListItem(matchingObjectsRaw, func(item runtime.Object) error { - objName := item.(*unstructured.Unstructured).GetName() - resourceNames = append(resourceNames, objName) - return nil - }) - // construct the actual query queryResults, err := p.buildQuery(info, namespace, resourceNames...) if err != nil { return nil, err } - return p.metricsFor(queryResults, info, matchingObjectsRaw) -} -func (p *prometheusProvider) GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error) { - info := provider.CustomMetricInfo{ - GroupResource: groupResource, - Metric: metricName, - Namespaced: false, - } - - return p.getSingle(info, "", name) -} - -func (p *prometheusProvider) GetRootScopedMetricBySelector(groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) { - info := provider.CustomMetricInfo{ - GroupResource: groupResource, - Metric: metricName, - Namespaced: false, - } - return p.getMultiple(info, "", selector) -} - -func (p *prometheusProvider) GetNamespacedMetricByName(groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error) { - info := provider.CustomMetricInfo{ - GroupResource: groupResource, - Metric: metricName, - Namespaced: true, - } - - return p.getSingle(info, namespace, name) -} - -func (p *prometheusProvider) GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) { - info := provider.CustomMetricInfo{ - GroupResource: groupResource, - Metric: metricName, - Namespaced: true, - } - return p.getMultiple(info, namespace, selector) + // return the resulting metrics + return p.metricsFor(queryResults, info, namespace, resourceNames) } type cachingMetricsLister struct { diff --git a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver/apiserver.go b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver/apiserver.go index 9292f158..99238953 100644 --- a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver/apiserver.go +++ b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver/apiserver.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/version" genericapiserver "k8s.io/apiserver/pkg/server" + "k8s.io/client-go/informers" "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider" cminstall "k8s.io/metrics/pkg/apis/custom_metrics/install" @@ -69,12 +70,12 @@ type completedConfig struct { } // Complete fills in any fields not set that are required to have valid data. It's mutating the receiver. -func (c *Config) Complete() completedConfig { +func (c *Config) Complete(informers informers.SharedInformerFactory) completedConfig { c.GenericConfig.Version = &version.Info{ Major: "1", Minor: "0", } - return completedConfig{c.GenericConfig.Complete(nil)} + return completedConfig{c.GenericConfig.Complete(informers)} } // New returns a new instance of CustomMetricsAdapterServer from the given config. diff --git a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/builder.go b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/builder.go new file mode 100644 index 00000000..3bcd11db --- /dev/null +++ b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/builder.go @@ -0,0 +1,283 @@ +/* +Copyright 2018 The Kubernetes 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. +*/ + +package cmd + +import ( + "fmt" + "sync" + "time" + + "github.com/spf13/pflag" + apimeta "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver" + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server" + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/dynamicmapper" + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider" +) + +// AdapterBase provides a base set of functionality for any custom metrics adapter. +// Embed it in a struct containing your options, then: +// +// - Use Flags() to add flags, then call Flags().Parse(os.Argv) +// - Use DynamicClient and RESTMapper to fetch handles to common utilities +// - Use WithCustomMetrics(provider) and WithExternalMetrics(provider) to install metrics providers +// - Use Run(stopChannel) to start the server +// +// All methods on this struct are idempotent except for Run -- they'll perform any +// initialization on the first call, then return the existing object on later calls. +// Methods on this struct are not safe to call from multiple goroutines without +// external synchronization. +type AdapterBase struct { + *server.CustomMetricsAdapterServerOptions + + // Name is the name of the API server. It defaults to custom-metrics-adapter + Name string + + // RemoteKubeConfigFile specifies the kubeconfig to use to construct + // the dynamic client and RESTMapper. It's set from a flag. + RemoteKubeConfigFile string + // DiscoveryInterval specifies the interval at which to recheck discovery + // information for the discovery RESTMapper. It's set from a flag. + DiscoveryInterval time.Duration + + // FlagSet is the flagset to add flags to. + // It defaults to the normal CommandLine flags + // if not explicitly set. + FlagSet *pflag.FlagSet + + // flagOnce controls initialization of the flags. + flagOnce sync.Once + + clientConfig *rest.Config + discoveryClient discovery.DiscoveryInterface + restMapper apimeta.RESTMapper + dynamicClient dynamic.Interface + informers informers.SharedInformerFactory + + config *apiserver.Config + server *apiserver.CustomMetricsAdapterServer + + cmProvider provider.CustomMetricsProvider + emProvider provider.ExternalMetricsProvider +} + +// InstallFlags installs the minimum required set of flags into the flagset. +func (b *AdapterBase) InstallFlags() { + b.initFlagSet() + b.flagOnce.Do(func() { + if b.CustomMetricsAdapterServerOptions == nil { + b.CustomMetricsAdapterServerOptions = server.NewCustomMetricsAdapterServerOptions() + } + + b.SecureServing.AddFlags(b.FlagSet) + b.Authentication.AddFlags(b.FlagSet) + b.Authorization.AddFlags(b.FlagSet) + b.Features.AddFlags(b.FlagSet) + + b.FlagSet.StringVar(&b.RemoteKubeConfigFile, "lister-kubeconfig", b.RemoteKubeConfigFile, + "kubeconfig file pointing at the 'core' kubernetes server with enough rights to list "+ + "any described objects") + b.FlagSet.DurationVar(&b.DiscoveryInterval, "discovery-interval", b.DiscoveryInterval, + "interval at which to refresh API discovery information") + }) +} + +// initFlagSet populates the flagset to the CommandLine flags if it's not already set. +func (b *AdapterBase) initFlagSet() { + if b.FlagSet == nil { + // default to the normal commandline flags + b.FlagSet = pflag.CommandLine + } +} + +// Flags returns the flagset used by this adapter. +// It will initialize the flagset with the minimum required set +// of flags as well. +func (b *AdapterBase) Flags() *pflag.FlagSet { + b.initFlagSet() + b.InstallFlags() + + return b.FlagSet +} + +// ClientConfig returns the REST client configuration used to construct +// clients for the clients and RESTMapper, and may be used for other +// purposes as well. If you need to mutate it, be sure to copy it with +// rest.CopyConfig first. +func (b *AdapterBase) ClientConfig() (*rest.Config, error) { + if b.clientConfig == nil { + var clientConfig *rest.Config + var err error + if len(b.RemoteKubeConfigFile) > 0 { + loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: b.RemoteKubeConfigFile} + loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) + + clientConfig, err = loader.ClientConfig() + } else { + clientConfig, err = rest.InClusterConfig() + } + if err != nil { + return nil, fmt.Errorf("unable to construct lister client config to initialize provider: %v", err) + } + b.clientConfig = clientConfig + } + return b.clientConfig, nil +} + +// DiscoveryClient returns a DiscoveryInterface suitable to for discovering resources +// available on the cluster. +func (b *AdapterBase) DiscoveryClient() (discovery.DiscoveryInterface, error) { + if b.discoveryClient == nil { + clientConfig, err := b.ClientConfig() + if err != nil { + return nil, err + } + discoveryClient, err := discovery.NewDiscoveryClientForConfig(clientConfig) + if err != nil { + return nil, fmt.Errorf("unable to construct discovery client for dynamic client: %v", err) + } + b.discoveryClient = discoveryClient + } + return b.discoveryClient, nil +} + +// RESTMapper returns a RESTMapper dynamically populated with discovery information. +// The discovery information will be periodically repopulated according to DiscoveryInterval. +func (b *AdapterBase) RESTMapper() (apimeta.RESTMapper, error) { + if b.restMapper == nil { + discoveryClient, err := b.DiscoveryClient() + if err != nil { + return nil, err + } + // NB: since we never actually look at the contents of + // the objects we fetch (beyond ObjectMeta), unstructured should be fine + dynamicMapper, err := dynamicmapper.NewRESTMapper(discoveryClient, b.DiscoveryInterval) + if err != nil { + return nil, fmt.Errorf("unable to construct dynamic discovery mapper: %v", err) + } + + b.restMapper = dynamicMapper + } + return b.restMapper, nil +} + +// DynamicClient returns a dynamic Kubernetes client capable of listing and fetching +// any resources on the cluster. +func (b *AdapterBase) DynamicClient() (dynamic.Interface, error) { + if b.dynamicClient == nil { + clientConfig, err := b.ClientConfig() + if err != nil { + return nil, err + } + dynClient, err := dynamic.NewForConfig(clientConfig) + if err != nil { + return nil, fmt.Errorf("unable to construct lister client to initialize provider: %v", err) + } + b.dynamicClient = dynClient + } + return b.dynamicClient, nil +} + +// WithCustomMetrics populates the custom metrics provider for this adapter. +func (b *AdapterBase) WithCustomMetrics(p provider.CustomMetricsProvider) { + b.cmProvider = p +} + +// WithExternalMetrics populates the external metrics provider for this adapter. +func (b *AdapterBase) WithExternalMetrics(p provider.ExternalMetricsProvider) { + b.emProvider = p +} + +// Config fetches the configuration used to ulitmately create the custom metrics adapter's +// API server. While this method is idempotent, it does "cement" values of some of the other +// fields, so make sure to only call it just before `Server` or `Run`. +// Normal users should not need to call this method -- it's for advanced use cases. +func (b *AdapterBase) Config() (*apiserver.Config, error) { + if b.config == nil { + b.InstallFlags() // just to be sure + + config, err := b.CustomMetricsAdapterServerOptions.Config() + if err != nil { + return nil, err + } + b.config = config + } + + return b.config, nil +} + +// Server fetches API server object used to ulitmately run the custom metrics adapter. +// While this method is idempotent, it does "cement" values of some of the other +// fields, so make sure to only call it just before `Run`. +// Normal users should not need to call this method -- it's for advanced use cases. +func (b *AdapterBase) Server() (*apiserver.CustomMetricsAdapterServer, error) { + if b.server == nil { + config, err := b.Config() + if err != nil { + return nil, err + } + + if b.Name == "" { + b.Name = "custom-metrics-adapter" + } + + // we add in the informers if they're not nil, but we don't try and + // construct them if the user didn't ask for them + server, err := config.Complete(b.informers).New(b.Name, b.cmProvider, b.emProvider) + if err != nil { + return nil, err + } + b.server = server + } + + return b.server, nil +} + +// Informers returns a SharedInformerFactory for constructing new informers. +// The informers will be automatically started as part of starting the adapter. +func (b *AdapterBase) Informers() (informers.SharedInformerFactory, error) { + if b.informers == nil { + clientConfig, err := b.ClientConfig() + if err != nil { + return nil, err + } + kubeClient, err := kubernetes.NewForConfig(clientConfig) + if err != nil { + return nil, err + } + b.informers = informers.NewSharedInformerFactory(kubeClient, 0) + } + + return b.informers, nil +} + +// Run runs this custom metrics adapter until the given stop channel is closed. +func (b *AdapterBase) Run(stopCh <-chan struct{}) error { + server, err := b.Server() + if err != nil { + return err + } + + return server.GenericAPIServer.PrepareRun().Run(stopCh) +} diff --git a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server/start.go b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server/start.go index 50a8a9c6..42ef3ac4 100644 --- a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server/start.go +++ b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server/start.go @@ -18,7 +18,6 @@ package server import ( "fmt" - "io" "net" "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver" @@ -32,20 +31,14 @@ type CustomMetricsAdapterServerOptions struct { Authentication *genericoptions.DelegatingAuthenticationOptions Authorization *genericoptions.DelegatingAuthorizationOptions Features *genericoptions.FeatureOptions - - StdOut io.Writer - StdErr io.Writer } -func NewCustomMetricsAdapterServerOptions(out, errOut io.Writer) *CustomMetricsAdapterServerOptions { +func NewCustomMetricsAdapterServerOptions() *CustomMetricsAdapterServerOptions { o := &CustomMetricsAdapterServerOptions{ SecureServing: genericoptions.WithLoopback(genericoptions.NewSecureServingOptions()), Authentication: genericoptions.NewDelegatingAuthenticationOptions(), Authorization: genericoptions.NewDelegatingAuthorizationOptions(), Features: genericoptions.NewFeatureOptions(), - - StdOut: out, - StdErr: errOut, } return o diff --git a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/helpers/helpers.go b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/helpers/helpers.go new file mode 100644 index 00000000..4c2533c9 --- /dev/null +++ b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/helpers/helpers.go @@ -0,0 +1,106 @@ +/* +Copyright 2018 The Kubernetes 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. +*/ + +package helpers + +import ( + "fmt" + + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" + "k8s.io/metrics/pkg/apis/custom_metrics" + + "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider" +) + +// ResourceFor attempts to resolve a single qualified resource for the given metric. +// You can use this to resolve a particular piece of CustomMetricInfo to the underlying +// resource that it describes, so that you can list matching objects in the cluster. +func ResourceFor(mapper apimeta.RESTMapper, info provider.CustomMetricInfo) (schema.GroupVersionResource, error) { + fullResources, err := mapper.ResourcesFor(info.GroupResource.WithVersion("")) + if err == nil && len(fullResources) == 0 { + err = fmt.Errorf("no fully versioned resources known for group-resource %v", info.GroupResource) + } + if err != nil { + return schema.GroupVersionResource{}, fmt.Errorf("unable to find preferred version to list matching resource names: %v", err) + } + + return fullResources[0], nil +} + +// ReferenceFor returns a new ObjectReference for the given group-resource and name. +// The group-resource is converted into a group-version-kind using the given RESTMapper. +// You can use this to easily construct an object reference for use in the DescribedObject +// field of CustomMetricInfo. +func ReferenceFor(mapper apimeta.RESTMapper, name types.NamespacedName, info provider.CustomMetricInfo) (custom_metrics.ObjectReference, error) { + kind, err := mapper.KindFor(info.GroupResource.WithVersion("")) + if err != nil { + return custom_metrics.ObjectReference{}, err + } + + // NB: return straight value, not a reference, so that the object can easily + // be copied for use multiple times with a different name. + return custom_metrics.ObjectReference{ + APIVersion: kind.Group + "/" + kind.Version, + Kind: kind.Kind, + Name: name.Name, + Namespace: name.Namespace, + }, nil +} + +// ListObjectNames uses the given dynamic client to list the names of all objects +// of the given resource matching the given selector. Namespace may be empty +// if the metric is for a root-scoped resource. +func ListObjectNames(mapper apimeta.RESTMapper, client dynamic.Interface, namespace string, selector labels.Selector, info provider.CustomMetricInfo) ([]string, error) { + res, err := ResourceFor(mapper, info) + if err != nil { + return nil, err + } + + var resClient dynamic.ResourceInterface + if info.Namespaced { + resClient = client.Resource(res).Namespace(namespace) + } else { + resClient = client.Resource(res) + } + + matchingObjectsRaw, err := resClient.List(metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return nil, err + } + + if !apimeta.IsListType(matchingObjectsRaw) { + return nil, fmt.Errorf("result of label selector list operation was not a list") + } + + var names []string + err = apimeta.EachListItem(matchingObjectsRaw, func(item runtime.Object) error { + objName := item.(*unstructured.Unstructured).GetName() + names = append(names, objName) + return nil + }) + if err != nil { + return nil, err + } + + return names, nil +} diff --git a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/interfaces.go b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/interfaces.go index a72cae79..d6874789 100644 --- a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/interfaces.go +++ b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/interfaces.go @@ -22,6 +22,7 @@ import ( apimeta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "k8s.io/metrics/pkg/apis/custom_metrics" "k8s.io/metrics/pkg/apis/external_metrics" ) @@ -37,7 +38,6 @@ type CustomMetricInfo struct { // ExternalMetricInfo describes a metric. type ExternalMetricInfo struct { Metric string - Labels map[string]string } func (i CustomMetricInfo) String() string { @@ -82,19 +82,13 @@ func (i CustomMetricInfo) Normalized(mapper apimeta.RESTMapper) (normalizedInfo // they may wish to query the main Kubernetes API server, or may // wish to simply make use of stored information in their TSDB. type CustomMetricsProvider interface { - // GetRootScopedMetricByName fetches a particular metric for a particular root-scoped object. - GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error) + // GetMetricByName fetches a particular metric for a particular object. + // The namespace will be empty if the metric is root-scoped. + GetMetricByName(name types.NamespacedName, info CustomMetricInfo) (*custom_metrics.MetricValue, error) - // GetRootScopedMetricByName fetches a particular metric for a set of root-scoped objects - // matching the given label selector. - GetRootScopedMetricBySelector(groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) - - // GetNamespacedMetricByName fetches a particular metric for a particular namespaced object. - GetNamespacedMetricByName(groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error) - - // GetNamespacedMetricByName fetches a particular metric for a set of namespaced objects - // matching the given label selector. - GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) + // GetMetricBySelector fetches a particular metric for a set of objects matching + // the given label selector. The namespace will be empty if the metric is root-scoped. + GetMetricBySelector(namespace string, selector labels.Selector, info CustomMetricInfo) (*custom_metrics.MetricValueList, error) // ListAllMetrics provides a list of all available metrics at // the current time. Note that this is not allowed to return @@ -108,7 +102,7 @@ type CustomMetricsProvider interface { // implementation how to translate metricSelector to a filter for metric values. // Namespace can be used by the implemetation for metric identification, access control or ignored. type ExternalMetricsProvider interface { - GetExternalMetric(namespace string, metricName string, metricSelector labels.Selector) (*external_metrics.ExternalMetricValueList, error) + GetExternalMetric(namespace string, metricSelector labels.Selector, info ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error) ListAllExternalMetrics() []ExternalMetricInfo } diff --git a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/registry/custom_metrics/reststorage.go b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/registry/custom_metrics/reststorage.go index da84e44c..8833cec7 100644 --- a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/registry/custom_metrics/reststorage.go +++ b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/registry/custom_metrics/reststorage.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" @@ -103,14 +104,11 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption } func (r *REST) handleIndividualOp(namespace string, groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValueList, error) { - var err error - var singleRes *custom_metrics.MetricValue - if namespace == "" { - singleRes, err = r.cmProvider.GetRootScopedMetricByName(groupResource, name, metricName) - } else { - singleRes, err = r.cmProvider.GetNamespacedMetricByName(groupResource, namespace, name, metricName) - } - + singleRes, err := r.cmProvider.GetMetricByName(types.NamespacedName{Namespace: namespace, Name: name}, provider.CustomMetricInfo{ + GroupResource: groupResource, + Metric: metricName, + Namespaced: namespace != "", + }) if err != nil { return nil, err } @@ -121,9 +119,9 @@ func (r *REST) handleIndividualOp(namespace string, groupResource schema.GroupRe } func (r *REST) handleWildcardOp(namespace string, groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) { - if namespace == "" { - return r.cmProvider.GetRootScopedMetricBySelector(groupResource, selector, metricName) - } else { - return r.cmProvider.GetNamespacedMetricBySelector(groupResource, namespace, selector, metricName) - } + return r.cmProvider.GetMetricBySelector(namespace, selector, provider.CustomMetricInfo{ + GroupResource: groupResource, + Metric: metricName, + Namespaced: namespace != "", + }) } diff --git a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/registry/external_metrics/reststorage.go b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/registry/external_metrics/reststorage.go index 7f565045..13c9c480 100644 --- a/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/registry/external_metrics/reststorage.go +++ b/vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/registry/external_metrics/reststorage.go @@ -76,5 +76,5 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption } metricName := requestInfo.Resource - return r.emProvider.GetExternalMetric(namespace, metricName, metricSelector) + return r.emProvider.GetExternalMetric(namespace, metricSelector, provider.ExternalMetricInfo{Metric: metricName}) }