mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 01:38:10 +00:00
Upgrade boilerplate to latest
The latest boilerplate comes with a lot of simplifications and helpers that let us reduce the amount of code written.
This commit is contained in:
parent
6b2c04dd61
commit
d02384477a
12 changed files with 604 additions and 381 deletions
16
Gopkg.lock
generated
16
Gopkg.lock
generated
|
|
@ -279,20 +279,22 @@
|
||||||
version = "v1.1.5"
|
version = "v1.1.5"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:46bdda4b93f3a1169365a10a9eef6cfba14fbb59a636f8562c6eba578afb48b8"
|
digest = "1:cf71ca07e2ee5914c776d9eaec4a7f63f4ddb013d55e39ffd5689fe4cd2df861"
|
||||||
name = "github.com/kubernetes-incubator/custom-metrics-apiserver"
|
name = "github.com/kubernetes-incubator/custom-metrics-apiserver"
|
||||||
packages = [
|
packages = [
|
||||||
"pkg/apiserver",
|
"pkg/apiserver",
|
||||||
"pkg/apiserver/installer",
|
"pkg/apiserver/installer",
|
||||||
|
"pkg/cmd",
|
||||||
"pkg/cmd/server",
|
"pkg/cmd/server",
|
||||||
"pkg/dynamicmapper",
|
"pkg/dynamicmapper",
|
||||||
"pkg/provider",
|
"pkg/provider",
|
||||||
|
"pkg/provider/helpers",
|
||||||
"pkg/registry/custom_metrics",
|
"pkg/registry/custom_metrics",
|
||||||
"pkg/registry/external_metrics",
|
"pkg/registry/external_metrics",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "d8f23423aa1d0ff2bc9656da863d721725b3c68a"
|
revision = "f54b0d6f31d8e0f0a2d7be372ddd837a2ef15d97"
|
||||||
version = "kubernetes-1.11.0-rc.1"
|
version = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|
@ -937,9 +939,9 @@
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
input-imports = [
|
input-imports = [
|
||||||
"github.com/golang/glog",
|
"github.com/golang/glog",
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/server",
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd",
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/dynamicmapper",
|
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider",
|
"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/client_golang/prometheus",
|
||||||
"github.com/prometheus/common/model",
|
"github.com/prometheus/common/model",
|
||||||
"github.com/spf13/cobra",
|
"github.com/spf13/cobra",
|
||||||
|
|
@ -952,14 +954,12 @@
|
||||||
"k8s.io/apimachinery/pkg/api/meta",
|
"k8s.io/apimachinery/pkg/api/meta",
|
||||||
"k8s.io/apimachinery/pkg/api/resource",
|
"k8s.io/apimachinery/pkg/api/resource",
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1",
|
"k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
|
||||||
"k8s.io/apimachinery/pkg/labels",
|
"k8s.io/apimachinery/pkg/labels",
|
||||||
"k8s.io/apimachinery/pkg/runtime",
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema",
|
"k8s.io/apimachinery/pkg/runtime/schema",
|
||||||
|
"k8s.io/apimachinery/pkg/types",
|
||||||
"k8s.io/apimachinery/pkg/util/runtime",
|
"k8s.io/apimachinery/pkg/util/runtime",
|
||||||
"k8s.io/apimachinery/pkg/util/wait",
|
"k8s.io/apimachinery/pkg/util/wait",
|
||||||
"k8s.io/apiserver/pkg/util/logs",
|
"k8s.io/apiserver/pkg/util/logs",
|
||||||
"k8s.io/client-go/discovery",
|
|
||||||
"k8s.io/client-go/dynamic",
|
"k8s.io/client-go/dynamic",
|
||||||
"k8s.io/client-go/dynamic/fake",
|
"k8s.io/client-go/dynamic/fake",
|
||||||
"k8s.io/client-go/rest",
|
"k8s.io/client-go/rest",
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@
|
||||||
|
|
||||||
# Kubernetes incubator deps
|
# Kubernetes incubator deps
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
version = "kubernetes-1.11.0-rc.1"
|
|
||||||
name = "github.com/kubernetes-incubator/custom-metrics-apiserver"
|
name = "github.com/kubernetes-incubator/custom-metrics-apiserver"
|
||||||
|
version = "kubernetes-1.11.2"
|
||||||
|
|
||||||
# Core Kubernetes deps
|
# Core Kubernetes deps
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
|
|
@ -72,6 +72,11 @@
|
||||||
name = "k8s.io/metrics"
|
name = "k8s.io/metrics"
|
||||||
version = "kubernetes-1.11.3"
|
version = "kubernetes-1.11.3"
|
||||||
|
|
||||||
|
# messed up kubernetes dep
|
||||||
|
[[override]]
|
||||||
|
name = "github.com/json-iterator/go"
|
||||||
|
version = "1.1.5"
|
||||||
|
|
||||||
# Test deps
|
# Test deps
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
|
|
|
||||||
|
|
@ -18,26 +18,165 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"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/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/util/logs"
|
"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() {
|
func main() {
|
||||||
logs.InitLogs()
|
logs.InitLogs()
|
||||||
defer logs.FlushLogs()
|
defer logs.FlushLogs()
|
||||||
|
|
||||||
if len(os.Getenv("GOMAXPROCS")) == 0 {
|
// set up flags
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
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)
|
// attach the provider to the server and run it
|
||||||
cmd.Flags().AddGoFlagSet(flag.CommandLine)
|
cmd.WithCustomMetrics(cmProvider)
|
||||||
if err := cmd.Execute(); err != nil {
|
if err := cmd.Run(wait.NeverStop); err != nil {
|
||||||
panic(err)
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -23,15 +23,14 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
"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"
|
pmodel "github.com/prometheus/common/model"
|
||||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
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/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
|
|
@ -76,52 +75,38 @@ func NewPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.Interfa
|
||||||
}, lister
|
}, lister
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prometheusProvider) metricFor(value pmodel.SampleValue, groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error) {
|
func (p *prometheusProvider) metricFor(value pmodel.SampleValue, name types.NamespacedName, info provider.CustomMetricInfo) (*custom_metrics.MetricValue, error) {
|
||||||
kind, err := p.mapper.KindFor(groupResource.WithVersion(""))
|
ref, err := helpers.ReferenceFor(p.mapper, name, info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &custom_metrics.MetricValue{
|
return &custom_metrics.MetricValue{
|
||||||
DescribedObject: custom_metrics.ObjectReference{
|
DescribedObject: ref,
|
||||||
APIVersion: groupResource.Group + "/" + runtime.APIVersionInternal,
|
MetricName: info.Metric,
|
||||||
Kind: kind.Kind,
|
// TODO(directxman12): use the right timestamp
|
||||||
Name: name,
|
Timestamp: metav1.Time{time.Now()},
|
||||||
Namespace: namespace,
|
Value: *resource.NewMilliQuantity(int64(value*1000.0), resource.DecimalSI),
|
||||||
},
|
|
||||||
MetricName: metricName,
|
|
||||||
Timestamp: metav1.Time{time.Now()},
|
|
||||||
Value: *resource.NewMilliQuantity(int64(value*1000.0), resource.DecimalSI),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prometheusProvider) metricsFor(valueSet pmodel.Vector, info provider.CustomMetricInfo, list runtime.Object) (*custom_metrics.MetricValueList, error) {
|
func (p *prometheusProvider) metricsFor(valueSet pmodel.Vector, info provider.CustomMetricInfo, namespace string, names []string) (*custom_metrics.MetricValueList, error) {
|
||||||
if !apimeta.IsListType(list) {
|
|
||||||
return nil, apierr.NewInternalError(fmt.Errorf("result of label selector list operation was not a list"))
|
|
||||||
}
|
|
||||||
|
|
||||||
values, found := p.MatchValuesToNames(info, valueSet)
|
values, found := p.MatchValuesToNames(info, valueSet)
|
||||||
if !found {
|
if !found {
|
||||||
return nil, provider.NewMetricNotFoundError(info.GroupResource, info.Metric)
|
return nil, provider.NewMetricNotFoundError(info.GroupResource, info.Metric)
|
||||||
}
|
}
|
||||||
res := []custom_metrics.MetricValue{}
|
res := []custom_metrics.MetricValue{}
|
||||||
|
|
||||||
err := apimeta.EachListItem(list, func(item runtime.Object) error {
|
for _, name := range names {
|
||||||
objUnstructured := item.(*unstructured.Unstructured)
|
if _, found := values[name]; !found {
|
||||||
objName := objUnstructured.GetName()
|
continue
|
||||||
if _, found := values[objName]; !found {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
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 {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
res = append(res, *value)
|
res = append(res, *value)
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &custom_metrics.MetricValueList{
|
return &custom_metrics.MetricValueList{
|
||||||
|
|
@ -151,14 +136,16 @@ func (p *prometheusProvider) buildQuery(info provider.CustomMetricInfo, namespac
|
||||||
return *queryResults.Vector, nil
|
return *queryResults.Vector, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prometheusProvider) getSingle(info provider.CustomMetricInfo, namespace, name string) (*custom_metrics.MetricValue, error) {
|
func (p *prometheusProvider) GetMetricByName(name types.NamespacedName, info provider.CustomMetricInfo) (*custom_metrics.MetricValue, error) {
|
||||||
queryResults, err := p.buildQuery(info, namespace, name)
|
// construct a query
|
||||||
|
queryResults, err := p.buildQuery(info, name.Namespace, name.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// associate the metrics
|
||||||
if len(queryResults) < 1 {
|
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)
|
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)
|
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 {
|
if !nameFound {
|
||||||
glog.Errorf("None of the results returned by when fetching metric %s for %q matched the resource name", info.String(), name)
|
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) {
|
func (p *prometheusProvider) GetMetricBySelector(namespace string, selector labels.Selector, info provider.CustomMetricInfo) (*custom_metrics.MetricValueList, error) {
|
||||||
fullResources, err := p.mapper.ResourcesFor(info.GroupResource.WithVersion(""))
|
// fetch a list of relevant resource names
|
||||||
if err == nil && len(fullResources) == 0 {
|
resourceNames, err := helpers.ListObjectNames(p.mapper, p.kubeClient, namespace, selector, info)
|
||||||
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()})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("unable to list matching resource names: %v", err)
|
glog.Errorf("unable to list matching resource names: %v", err)
|
||||||
// don't leak implementation details to the user
|
// don't leak implementation details to the user
|
||||||
return nil, apierr.NewInternalError(fmt.Errorf("unable to list matching resources"))
|
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
|
// construct the actual query
|
||||||
queryResults, err := p.buildQuery(info, namespace, resourceNames...)
|
queryResults, err := p.buildQuery(info, namespace, resourceNames...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return p.metricsFor(queryResults, info, matchingObjectsRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prometheusProvider) GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error) {
|
// return the resulting metrics
|
||||||
info := provider.CustomMetricInfo{
|
return p.metricsFor(queryResults, info, namespace, resourceNames)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type cachingMetricsLister struct {
|
type cachingMetricsLister struct {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
"k8s.io/apimachinery/pkg/version"
|
"k8s.io/apimachinery/pkg/version"
|
||||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
||||||
cminstall "k8s.io/metrics/pkg/apis/custom_metrics/install"
|
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.
|
// 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{
|
c.GenericConfig.Version = &version.Info{
|
||||||
Major: "1",
|
Major: "1",
|
||||||
Minor: "0",
|
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.
|
// New returns a new instance of CustomMetricsAdapterServer from the given config.
|
||||||
|
|
|
||||||
283
vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/builder.go
generated
vendored
Normal file
283
vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/cmd/builder.go
generated
vendored
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,6 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver"
|
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver"
|
||||||
|
|
@ -32,20 +31,14 @@ type CustomMetricsAdapterServerOptions struct {
|
||||||
Authentication *genericoptions.DelegatingAuthenticationOptions
|
Authentication *genericoptions.DelegatingAuthenticationOptions
|
||||||
Authorization *genericoptions.DelegatingAuthorizationOptions
|
Authorization *genericoptions.DelegatingAuthorizationOptions
|
||||||
Features *genericoptions.FeatureOptions
|
Features *genericoptions.FeatureOptions
|
||||||
|
|
||||||
StdOut io.Writer
|
|
||||||
StdErr io.Writer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCustomMetricsAdapterServerOptions(out, errOut io.Writer) *CustomMetricsAdapterServerOptions {
|
func NewCustomMetricsAdapterServerOptions() *CustomMetricsAdapterServerOptions {
|
||||||
o := &CustomMetricsAdapterServerOptions{
|
o := &CustomMetricsAdapterServerOptions{
|
||||||
SecureServing: genericoptions.WithLoopback(genericoptions.NewSecureServingOptions()),
|
SecureServing: genericoptions.WithLoopback(genericoptions.NewSecureServingOptions()),
|
||||||
Authentication: genericoptions.NewDelegatingAuthenticationOptions(),
|
Authentication: genericoptions.NewDelegatingAuthenticationOptions(),
|
||||||
Authorization: genericoptions.NewDelegatingAuthorizationOptions(),
|
Authorization: genericoptions.NewDelegatingAuthorizationOptions(),
|
||||||
Features: genericoptions.NewFeatureOptions(),
|
Features: genericoptions.NewFeatureOptions(),
|
||||||
|
|
||||||
StdOut: out,
|
|
||||||
StdErr: errOut,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return o
|
return o
|
||||||
|
|
|
||||||
106
vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/helpers/helpers.go
generated
vendored
Normal file
106
vendor/github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/helpers/helpers.go
generated
vendored
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/metrics/pkg/apis/custom_metrics"
|
"k8s.io/metrics/pkg/apis/custom_metrics"
|
||||||
"k8s.io/metrics/pkg/apis/external_metrics"
|
"k8s.io/metrics/pkg/apis/external_metrics"
|
||||||
)
|
)
|
||||||
|
|
@ -37,7 +38,6 @@ type CustomMetricInfo struct {
|
||||||
// ExternalMetricInfo describes a metric.
|
// ExternalMetricInfo describes a metric.
|
||||||
type ExternalMetricInfo struct {
|
type ExternalMetricInfo struct {
|
||||||
Metric string
|
Metric string
|
||||||
Labels map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i CustomMetricInfo) 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
|
// they may wish to query the main Kubernetes API server, or may
|
||||||
// wish to simply make use of stored information in their TSDB.
|
// wish to simply make use of stored information in their TSDB.
|
||||||
type CustomMetricsProvider interface {
|
type CustomMetricsProvider interface {
|
||||||
// GetRootScopedMetricByName fetches a particular metric for a particular root-scoped object.
|
// GetMetricByName fetches a particular metric for a particular object.
|
||||||
GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error)
|
// 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
|
// GetMetricBySelector fetches a particular metric for a set of objects matching
|
||||||
// matching the given label selector.
|
// the given label selector. The namespace will be empty if the metric is root-scoped.
|
||||||
GetRootScopedMetricBySelector(groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error)
|
GetMetricBySelector(namespace string, selector labels.Selector, info CustomMetricInfo) (*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)
|
|
||||||
|
|
||||||
// ListAllMetrics provides a list of all available metrics at
|
// ListAllMetrics provides a list of all available metrics at
|
||||||
// the current time. Note that this is not allowed to return
|
// 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.
|
// 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.
|
// Namespace can be used by the implemetation for metric identification, access control or ignored.
|
||||||
type ExternalMetricsProvider interface {
|
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
|
ListAllExternalMetrics() []ExternalMetricInfo
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apiserver/pkg/endpoints/request"
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"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) {
|
func (r *REST) handleIndividualOp(namespace string, groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValueList, error) {
|
||||||
var err error
|
singleRes, err := r.cmProvider.GetMetricByName(types.NamespacedName{Namespace: namespace, Name: name}, provider.CustomMetricInfo{
|
||||||
var singleRes *custom_metrics.MetricValue
|
GroupResource: groupResource,
|
||||||
if namespace == "" {
|
Metric: metricName,
|
||||||
singleRes, err = r.cmProvider.GetRootScopedMetricByName(groupResource, name, metricName)
|
Namespaced: namespace != "",
|
||||||
} else {
|
})
|
||||||
singleRes, err = r.cmProvider.GetNamespacedMetricByName(groupResource, namespace, name, metricName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
func (r *REST) handleWildcardOp(namespace string, groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) {
|
||||||
if namespace == "" {
|
return r.cmProvider.GetMetricBySelector(namespace, selector, provider.CustomMetricInfo{
|
||||||
return r.cmProvider.GetRootScopedMetricBySelector(groupResource, selector, metricName)
|
GroupResource: groupResource,
|
||||||
} else {
|
Metric: metricName,
|
||||||
return r.cmProvider.GetNamespacedMetricBySelector(groupResource, namespace, selector, metricName)
|
Namespaced: namespace != "",
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,5 +76,5 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption
|
||||||
}
|
}
|
||||||
metricName := requestInfo.Resource
|
metricName := requestInfo.Resource
|
||||||
|
|
||||||
return r.emProvider.GetExternalMetric(namespace, metricName, metricSelector)
|
return r.emProvider.GetExternalMetric(namespace, metricSelector, provider.ExternalMetricInfo{Metric: metricName})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue