feat: add metrics-adapter component to support centralized hpa
Signed-off-by: jwcesign <jiangwei115@huawei.com>
This commit is contained in:
parent
a18702b1c8
commit
ae6c34578c
4
Makefile
4
Makefile
|
@ -17,7 +17,8 @@ TARGETS := karmada-aggregated-apiserver \
|
|||
karmada-scheduler-estimator \
|
||||
karmada-interpreter-webhook-example \
|
||||
karmada-search \
|
||||
karmada-operator
|
||||
karmada-operator \
|
||||
karmada-metrics-adapter
|
||||
|
||||
CTL_TARGETS := karmadactl kubectl-karmada
|
||||
|
||||
|
@ -122,6 +123,7 @@ endif
|
|||
docker push ${REGISTRY}/karmada-aggregated-apiserver:${VERSION}
|
||||
docker push ${REGISTRY}/karmada-search:${VERSION}
|
||||
docker push ${REGISTRY}/karmada-operator:${VERSION}
|
||||
docker push ${REGISTRY}/karmada-metrics-adapter:${VERSION}
|
||||
|
||||
# Build and package binary
|
||||
#
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"version": "unversioned"
|
||||
},
|
||||
"paths": {
|
||||
"/apis/": {
|
||||
"/apis": {
|
||||
"get": {
|
||||
"description": "get available API versions",
|
||||
"consumes": [
|
||||
|
@ -40,7 +40,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/apis/cluster.karmada.io/": {
|
||||
"/apis/cluster.karmada.io": {
|
||||
"get": {
|
||||
"description": "get information of a group",
|
||||
"consumes": [
|
||||
|
@ -70,7 +70,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/apis/cluster.karmada.io/v1alpha1/": {
|
||||
"/apis/cluster.karmada.io/v1alpha1": {
|
||||
"get": {
|
||||
"description": "get available resources",
|
||||
"consumes": [
|
||||
|
@ -1564,7 +1564,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/apis/config.karmada.io/": {
|
||||
"/apis/config.karmada.io": {
|
||||
"get": {
|
||||
"description": "get information of a group",
|
||||
"consumes": [
|
||||
|
@ -1594,7 +1594,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/apis/config.karmada.io/v1alpha1/": {
|
||||
"/apis/config.karmada.io/v1alpha1": {
|
||||
"get": {
|
||||
"description": "get available resources",
|
||||
"consumes": [
|
||||
|
@ -2624,7 +2624,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/apis/networking.karmada.io/": {
|
||||
"/apis/networking.karmada.io": {
|
||||
"get": {
|
||||
"description": "get information of a group",
|
||||
"consumes": [
|
||||
|
@ -2654,7 +2654,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/apis/networking.karmada.io/v1alpha1/": {
|
||||
"/apis/networking.karmada.io/v1alpha1": {
|
||||
"get": {
|
||||
"description": "get available resources",
|
||||
"consumes": [
|
||||
|
@ -3940,7 +3940,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/apis/policy.karmada.io/": {
|
||||
"/apis/policy.karmada.io": {
|
||||
"get": {
|
||||
"description": "get information of a group",
|
||||
"consumes": [
|
||||
|
@ -3970,7 +3970,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/apis/policy.karmada.io/v1alpha1/": {
|
||||
"/apis/policy.karmada.io/v1alpha1": {
|
||||
"get": {
|
||||
"description": "get available resources",
|
||||
"consumes": [
|
||||
|
@ -9768,7 +9768,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/apis/search.karmada.io/": {
|
||||
"/apis/search.karmada.io": {
|
||||
"get": {
|
||||
"description": "get information of a group",
|
||||
"consumes": [
|
||||
|
@ -9798,7 +9798,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/apis/search.karmada.io/v1alpha1/": {
|
||||
"/apis/search.karmada.io/v1alpha1": {
|
||||
"get": {
|
||||
"description": "get available resources",
|
||||
"consumes": [
|
||||
|
@ -10828,7 +10828,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/apis/work.karmada.io/": {
|
||||
"/apis/work.karmada.io": {
|
||||
"get": {
|
||||
"description": "get information of a group",
|
||||
"consumes": [
|
||||
|
@ -10858,7 +10858,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/apis/work.karmada.io/v1alpha1/": {
|
||||
"/apis/work.karmada.io/v1alpha1": {
|
||||
"get": {
|
||||
"description": "get available resources",
|
||||
"consumes": [
|
||||
|
@ -12144,7 +12144,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/apis/work.karmada.io/v1alpha2/": {
|
||||
"/apis/work.karmada.io/v1alpha2": {
|
||||
"get": {
|
||||
"description": "get available resources",
|
||||
"consumes": [
|
||||
|
@ -17081,8 +17081,13 @@
|
|||
"type": "string"
|
||||
},
|
||||
"pathType": {
|
||||
"description": "PathType determines the interpretation of the Path matching. PathType can be one of the following values: * Exact: Matches the URL path exactly. * Prefix: Matches based on a URL path prefix split by '/'. Matching is\n done on a path element by element basis. A path element refers is the\n list of labels in the path split by the '/' separator. A request is a\n match for path p if every p is an element-wise prefix of p of the\n request path. Note that if the last element of the path is a substring\n of the last element in request path, it is not a match (e.g. /foo/bar\n matches /foo/bar/baz, but does not match /foo/barbaz).\n* ImplementationSpecific: Interpretation of the Path matching is up to\n the IngressClass. Implementations can treat this as a separate PathType\n or treat it identically to Prefix or Exact path types.\nImplementations are required to support all path types.",
|
||||
"type": "string"
|
||||
"description": "PathType determines the interpretation of the Path matching. PathType can be one of the following values: * Exact: Matches the URL path exactly. * Prefix: Matches based on a URL path prefix split by '/'. Matching is\n done on a path element by element basis. A path element refers is the\n list of labels in the path split by the '/' separator. A request is a\n match for path p if every p is an element-wise prefix of p of the\n request path. Note that if the last element of the path is a substring\n of the last element in request path, it is not a match (e.g. /foo/bar\n matches /foo/bar/baz, but does not match /foo/barbaz).\n* ImplementationSpecific: Interpretation of the Path matching is up to\n the IngressClass. Implementations can treat this as a separate PathType\n or treat it identically to Prefix or Exact path types.\nImplementations are required to support all path types.\n\nPossible enum values:\n - `\"Exact\"` matches the URL path exactly and with case sensitivity.\n - `\"ImplementationSpecific\"` matching is up to the IngressClass. Implementations can treat this as a separate PathType or treat it identically to Prefix or Exact path types.\n - `\"Prefix\"` matches based on a URL path prefix split by '/'. Matching is case sensitive and done on a path element by element basis. A path element refers to the list of labels in the path split by the '/' separator. A request is a match for path p if every p is an element-wise prefix of p of the request path. Note that if the last element of the path is a substring of the last element in request path, it is not a match (e.g. /foo/bar matches /foo/bar/baz, but does not match /foo/barbaz). If multiple matching paths exist in an Ingress spec, the longest matching path is given priority. Examples: - /foo/bar does not match requests to /foo/barbaz - /foo/bar matches request to /foo/bar and /foo/bar/baz - /foo and /foo/ both match requests to /foo and /foo/. If both paths are present in an Ingress spec, the longest matching path (/foo/) is given priority.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Exact",
|
||||
"ImplementationSpecific",
|
||||
"Prefix"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: apiregistration.k8s.io/v1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1beta1.metrics.k8s.io
|
||||
labels:
|
||||
app: karmada-metrics-adapter
|
||||
apiserver: "true"
|
||||
spec:
|
||||
insecureSkipTLSVerify: true
|
||||
group: metrics.k8s.io
|
||||
groupPriorityMinimum: 2000
|
||||
service:
|
||||
name: karmada-metrics-adapter
|
||||
namespace: karmada-system
|
||||
version: v1beta1
|
||||
versionPriority: 10
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: karmada-metrics-adapter
|
||||
namespace: karmada-system
|
||||
spec:
|
||||
type: ExternalName
|
||||
externalName: karmada-metrics-adapter.karmada-system.svc.cluster.local
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: karmada-metrics-adapter
|
||||
namespace: karmada-system
|
||||
labels:
|
||||
app: karmada-metrics-adapter
|
||||
apiserver: "true"
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: karmada-metrics-adapter
|
||||
apiserver: "true"
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: karmada-metrics-adapter
|
||||
apiserver: "true"
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- name: karmada-metrics-adapter
|
||||
image: docker.io/karmada/karmada-metrics-adapter:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
volumeMounts:
|
||||
- name: karmada-certs
|
||||
mountPath: /etc/karmada/pki
|
||||
readOnly: true
|
||||
- name: kubeconfig
|
||||
subPath: kubeconfig
|
||||
mountPath: /etc/kubeconfig
|
||||
command:
|
||||
- /bin/karmada-metrics-adapter
|
||||
- --kubeconfig=/etc/kubeconfig
|
||||
- --authentication-kubeconfig=/etc/kubeconfig
|
||||
- --authorization-kubeconfig=/etc/kubeconfig
|
||||
- --client-ca-file=/etc/karmada/pki/ca.crt
|
||||
- --audit-log-path=-
|
||||
- --audit-log-maxage=0
|
||||
- --audit-log-maxbackup=0
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: 443
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 3
|
||||
timeoutSeconds: 15
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 443
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 10
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 15
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
volumes:
|
||||
- name: karmada-certs
|
||||
secret:
|
||||
secretName: karmada-cert-secret
|
||||
- name: kubeconfig
|
||||
secret:
|
||||
secretName: kubeconfig
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: karmada-metrics-adapter
|
||||
namespace: karmada-system
|
||||
labels:
|
||||
app: karmada-metrics-adapter
|
||||
apiserver: "true"
|
||||
spec:
|
||||
ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
targetPort: 443
|
||||
selector:
|
||||
app: karmada-metrics-adapter
|
|
@ -0,0 +1,62 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/component-base/term"
|
||||
|
||||
"github.com/karmada-io/karmada/cmd/metrics-adapter/app/options"
|
||||
"github.com/karmada-io/karmada/pkg/sharedcli"
|
||||
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
||||
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
||||
)
|
||||
|
||||
// NewMetricsAdapterCommand creates a *cobra.Command object with default parameters
|
||||
func NewMetricsAdapterCommand(ctx context.Context) *cobra.Command {
|
||||
opts := options.NewOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "karmada-metrics-adapter",
|
||||
Long: `The karmada-metrics-adapter is a adapter to aggregate the metrics from member clusters.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := opts.Complete(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := opts.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := opts.Run(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
for _, arg := range args {
|
||||
if len(arg) > 0 {
|
||||
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
fss := cliflag.NamedFlagSets{}
|
||||
|
||||
genericFlagSet := fss.FlagSet("generic")
|
||||
opts.AddFlags(genericFlagSet)
|
||||
|
||||
// Set klog flags
|
||||
logsFlagSet := fss.FlagSet("logs")
|
||||
klogflag.Add(logsFlagSet)
|
||||
|
||||
cmd.AddCommand(sharedcommand.NewCmdVersion("karmada-metrics-adapter"))
|
||||
cmd.Flags().AddFlagSet(genericFlagSet)
|
||||
cmd.Flags().AddFlagSet(logsFlagSet)
|
||||
|
||||
cols, _, _ := term.TerminalSize(cmd.OutOrStdout())
|
||||
sharedcli.SetUsageAndHelpFunc(cmd, fss, cols)
|
||||
return cmd
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package options
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/custom-metrics-apiserver/pkg/cmd/options"
|
||||
"sigs.k8s.io/metrics-server/pkg/api"
|
||||
|
||||
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
|
||||
informerfactory "github.com/karmada-io/karmada/pkg/generated/informers/externalversions"
|
||||
generatedopenapi "github.com/karmada-io/karmada/pkg/generated/openapi"
|
||||
"github.com/karmada-io/karmada/pkg/metricsadapter"
|
||||
"github.com/karmada-io/karmada/pkg/version"
|
||||
)
|
||||
|
||||
// Options contains everything necessary to create and run metrics-adapter.
|
||||
type Options struct {
|
||||
CustomMetricsAdapterServerOptions *options.CustomMetricsAdapterServerOptions
|
||||
|
||||
KubeConfig string
|
||||
}
|
||||
|
||||
// NewOptions builds a default metrics-adapter options.
|
||||
func NewOptions() *Options {
|
||||
o := &Options{
|
||||
CustomMetricsAdapterServerOptions: options.NewCustomMetricsAdapterServerOptions(),
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// Complete fills in fields required to have valid data.
|
||||
func (o *Options) Complete() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFlags adds flags to the specified FlagSet.
|
||||
func (o *Options) AddFlags(fs *pflag.FlagSet) {
|
||||
o.CustomMetricsAdapterServerOptions.AddFlags(fs)
|
||||
|
||||
fs.StringVar(&o.KubeConfig, "kubeconfig", o.KubeConfig, "Path to karmada control plane kubeconfig file.")
|
||||
}
|
||||
|
||||
// Config returns config for the metrics-adapter server given Options
|
||||
func (o *Options) Config() (*metricsadapter.MetricsServer, error) {
|
||||
restConfig, err := clientcmd.BuildConfigFromFlags("", o.KubeConfig)
|
||||
if err != nil {
|
||||
klog.Errorf("Unable to build restConfig: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
karmadaClient := karmadaclientset.NewForConfigOrDie(restConfig)
|
||||
factory := informerfactory.NewSharedInformerFactory(karmadaClient, 0)
|
||||
|
||||
metricsController := metricsadapter.NewMetricsController(restConfig, factory)
|
||||
metricsAdapter := metricsadapter.NewMetricsAdapter(metricsController, o.CustomMetricsAdapterServerOptions)
|
||||
metricsAdapter.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(api.Scheme))
|
||||
metricsAdapter.OpenAPIConfig.Info.Title = "karmada-metrics-adapter"
|
||||
metricsAdapter.OpenAPIConfig.Info.Version = "1.0.0"
|
||||
|
||||
server, err := metricsAdapter.Server()
|
||||
if err != nil {
|
||||
klog.Errorf("Unable to construct metrics adapter: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = server.GenericAPIServer.AddPostStartHook("start-karmada-informers", func(context genericapiserver.PostStartHookContext) error {
|
||||
factory.Start(context.StopCh)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
klog.Errorf("Unable to add post hook: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := api.Install(metricsAdapter, metricsAdapter.PodLister, metricsAdapter.NodeLister, server.GenericAPIServer, nil); err != nil {
|
||||
klog.Errorf("unable to install resource metrics adapter: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return metricsadapter.NewMetricsServer(metricsController, metricsAdapter), nil
|
||||
}
|
||||
|
||||
// Run runs the metrics-adapter with options. This should never exit.
|
||||
func (o *Options) Run(ctx context.Context) error {
|
||||
klog.Infof("karmada-metrics-adapter version: %s", version.Get())
|
||||
|
||||
metricsServer, err := o.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return metricsServer.StartServer(ctx.Done())
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package options
|
||||
|
||||
import (
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
)
|
||||
|
||||
// Validate checks Options and return a slice of found errs.
|
||||
func (o *Options) Validate() error {
|
||||
var errs []error
|
||||
|
||||
errs = append(errs, o.CustomMetricsAdapterServerOptions.Validate()...)
|
||||
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"k8s.io/component-base/cli"
|
||||
_ "k8s.io/component-base/logs/json/register" // for JSON log format registration
|
||||
controllerruntime "sigs.k8s.io/controller-runtime"
|
||||
|
||||
"github.com/karmada-io/karmada/cmd/metrics-adapter/app"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := controllerruntime.SetupSignalHandler()
|
||||
cmd := app.NewMetricsAdapterCommand(ctx)
|
||||
code := cli.Run(cmd)
|
||||
os.Exit(code)
|
||||
}
|
19
go.mod
19
go.mod
|
@ -17,7 +17,7 @@ require (
|
|||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/vektra/mockery/v2 v2.10.0
|
||||
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
|
||||
go.uber.org/atomic v1.9.0
|
||||
|
@ -38,16 +38,19 @@ require (
|
|||
k8s.io/code-generator v0.26.2
|
||||
k8s.io/component-base v0.26.2
|
||||
k8s.io/component-helpers v0.26.2
|
||||
k8s.io/klog/v2 v2.80.1
|
||||
k8s.io/klog/v2 v2.90.1
|
||||
k8s.io/kube-aggregator v0.26.2
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280
|
||||
k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d
|
||||
k8s.io/kubectl v0.26.2
|
||||
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
|
||||
k8s.io/metrics v0.26.2
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5
|
||||
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf
|
||||
sigs.k8s.io/cluster-api v1.4.0
|
||||
sigs.k8s.io/controller-runtime v0.14.5
|
||||
sigs.k8s.io/custom-metrics-apiserver v1.25.1-0.20230308103314-bd3192a29bc8
|
||||
sigs.k8s.io/kind v0.17.0
|
||||
sigs.k8s.io/mcs-api v0.1.0
|
||||
sigs.k8s.io/metrics-server v0.6.1-0.20230509102056-1a23b5bd2e12
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
@ -69,7 +72,7 @@ require (
|
|||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/fatih/camelcase v1.0.0 // indirect
|
||||
|
@ -80,8 +83,8 @@ require (
|
|||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-logr/zapr v1.2.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.1 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gobuffalo/flect v1.0.2 // indirect
|
||||
|
@ -175,7 +178,7 @@ require (
|
|||
k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect
|
||||
k8s.io/kms v0.26.2 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/kustomize/api v0.12.1 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect
|
||||
)
|
||||
|
|
39
go.sum
39
go.sum
|
@ -199,8 +199,8 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7fo
|
|||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
|
||||
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
|
||||
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -283,15 +283,15 @@ github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwds
|
|||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
|
||||
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||
github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
|
||||
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
|
@ -533,6 +533,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
|||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
@ -779,8 +780,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
|
@ -1400,6 +1402,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
|
@ -1487,22 +1490,24 @@ k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
|||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
|
||||
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
|
||||
k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kms v0.26.2 h1:GM1gg3tFK3OUU/QQFi93yGjG3lJT8s8l3Wkn2+VxBLM=
|
||||
k8s.io/kms v0.26.2/go.mod h1:69qGnf1NsFOQP07fBYqNLZklqEHSJF024JqYCaeVxHg=
|
||||
k8s.io/kube-aggregator v0.26.2 h1:WtcLGisa5aCKBbBI1/Xe7gdjPlVb5Xhvs4a8Rdk8EXs=
|
||||
k8s.io/kube-aggregator v0.26.2/go.mod h1:swDTw0k/XghVLR+PCWnP6Y36wR2+DsqL2HUVq8eu0RI=
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
|
||||
k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d h1:VcFq5n7wCJB2FQMCIHfC+f+jNcGgNMar1uKd6rVlifU=
|
||||
k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY=
|
||||
k8s.io/kubectl v0.26.2 h1:SMPB4j48eVFxsYluBq3VLyqXtE6b72YnszkbTAtFye4=
|
||||
k8s.io/kubectl v0.26.2/go.mod h1:KYWOXSwp2BrDn3kPeoU/uKzKtdqvhK1dgZGd0+no4cM=
|
||||
k8s.io/metrics v0.26.2 h1:2gUvUWWnHPdE2tyA5DvyHC8HGryr+izhY9i5dzLP06s=
|
||||
k8s.io/metrics v0.26.2/go.mod h1:PX1wm9REV9hSGuw9GcXTFNDgab1KRXck3mNeiLYbRho=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y=
|
||||
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk=
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0=
|
||||
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
@ -1517,8 +1522,10 @@ sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gE
|
|||
sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s=
|
||||
sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
|
||||
sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/custom-metrics-apiserver v1.25.1-0.20230308103314-bd3192a29bc8 h1:0dZXAPEWoIAJ3KtBHKAViBSwE1yMRH0PI/UdYb4bIhE=
|
||||
sigs.k8s.io/custom-metrics-apiserver v1.25.1-0.20230308103314-bd3192a29bc8/go.mod h1:9nUXR/EgdYZto1aQ6yhwOksPR7J979jSyOqic1IgaOo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/kind v0.8.1/go.mod h1:oNKTxUVPYkV9lWzY6CVMNluVq8cBsyq+UgPJdvA3uu4=
|
||||
sigs.k8s.io/kind v0.17.0 h1:CScmGz/wX66puA06Gj8OZb76Wmk7JIjgWf5JDvY7msM=
|
||||
sigs.k8s.io/kind v0.17.0/go.mod h1:Qqp8AiwOlMZmJWs37Hgs31xcbiYXjtXlRBSftcnZXQk=
|
||||
|
@ -1528,6 +1535,8 @@ sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2
|
|||
sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4=
|
||||
sigs.k8s.io/mcs-api v0.1.0 h1:edDbg0oRGfXw8TmZjKYep06LcJLv/qcYLidejnUp0PM=
|
||||
sigs.k8s.io/mcs-api v0.1.0/go.mod h1:gGiAryeFNB4GBsq2LBmVqSgKoobLxt+p7ii/WG5QYYw=
|
||||
sigs.k8s.io/metrics-server v0.6.1-0.20230509102056-1a23b5bd2e12 h1:M2W5nfr7ATwuEaFjH6qH0GeK2M1LoIw78kRvVxhEX+E=
|
||||
sigs.k8s.io/metrics-server v0.6.1-0.20230509102056-1a23b5bd2e12/go.mod h1:PuZhxxSf39LvZ27uzIaiUoGwaz/RoZzmXdQbi252D5Y=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
|
|
|
@ -197,6 +197,7 @@ openapi-gen \
|
|||
--input-dirs "k8s.io/apimachinery/pkg/apis/meta/v1,k8s.io/apimachinery/pkg/runtime,k8s.io/apimachinery/pkg/version" \
|
||||
--input-dirs "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,k8s.io/api/admissionregistration/v1,k8s.io/api/networking/v1" \
|
||||
--input-dirs "github.com/karmada-io/karmada/pkg/apis/search/v1alpha1" \
|
||||
--input-dirs "k8s.io/metrics/pkg/apis/custom_metrics,k8s.io/metrics/pkg/apis/custom_metrics/v1beta1,k8s.io/metrics/pkg/apis/custom_metrics/v1beta2,k8s.io/metrics/pkg/apis/external_metrics,k8s.io/metrics/pkg/apis/external_metrics/v1beta1,k8s.io/metrics/pkg/apis/metrics,k8s.io/metrics/pkg/apis/metrics/v1beta1" \
|
||||
--output-package "github.com/karmada-io/karmada/pkg/generated/openapi" \
|
||||
-O zz_generated.openapi
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ INTERPRETER_WEBHOOK_EXAMPLE_LABEL="karmada-interpreter-webhook-example"
|
|||
KARMADA_SEARCH_LABEL="karmada-search"
|
||||
KARMADA_OPENSEARCH_LABEL="karmada-opensearch"
|
||||
KARMADA_OPENSEARCH_DASHBOARDS_LABEL="karmada-opensearch-dashboards"
|
||||
KARMADA_METRICS_ADAPTER_LABEL="karmada-metrics-adapter"
|
||||
|
||||
KARMADA_GO_PACKAGE="github.com/karmada-io/karmada"
|
||||
|
||||
|
@ -37,6 +38,7 @@ KARMADA_TARGET_SOURCE=(
|
|||
karmada-interpreter-webhook-example=examples/customresourceinterpreter/webhook
|
||||
karmada-search=cmd/karmada-search
|
||||
karmada-operator=operator/cmd/operator
|
||||
karmada-metrics-adapter=cmd/metrics-adapter
|
||||
)
|
||||
|
||||
#https://textkool.com/en/ascii-art-generator?hl=default&vl=default&font=DOS%20Rebel&text=KARMADA
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
|||
package metricsadapter
|
||||
|
||||
import (
|
||||
basecmd "sigs.k8s.io/custom-metrics-apiserver/pkg/cmd"
|
||||
"sigs.k8s.io/custom-metrics-apiserver/pkg/cmd/options"
|
||||
|
||||
"github.com/karmada-io/karmada/pkg/metricsadapter/provider"
|
||||
)
|
||||
|
||||
// MetricsAdapter is a metrics adapter to provider native metrics, custom metrics and external metrics
|
||||
type MetricsAdapter struct {
|
||||
basecmd.AdapterBase
|
||||
|
||||
*provider.ResourceMetricsProvider
|
||||
}
|
||||
|
||||
// NewMetricsAdapter creates a new metrics adapter
|
||||
func NewMetricsAdapter(controller *MetricsController, customMetricsAdapterServerOptions *options.CustomMetricsAdapterServerOptions) *MetricsAdapter {
|
||||
adapter := &MetricsAdapter{}
|
||||
adapter.CustomMetricsAdapterServerOptions = customMetricsAdapterServerOptions
|
||||
|
||||
adapter.ResourceMetricsProvider = provider.NewResourceMetricsProvider(controller.ClusterLister, controller.InformerManager)
|
||||
customProvider := provider.MakeCustomMetricsProvider()
|
||||
externalProvider := provider.MakeExternalMetricsProvider()
|
||||
adapter.WithCustomMetrics(customProvider)
|
||||
adapter.WithExternalMetrics(externalProvider)
|
||||
|
||||
return adapter
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package metricsadapter
|
||||
|
||||
// MetricsServer is a metrics server
|
||||
type MetricsServer struct {
|
||||
metricsController *MetricsController
|
||||
metricsAdapter *MetricsAdapter
|
||||
}
|
||||
|
||||
// NewMetricsServer creates a new metrics server
|
||||
func NewMetricsServer(controller *MetricsController, metricsAdapter *MetricsAdapter) *MetricsServer {
|
||||
return &MetricsServer{
|
||||
metricsController: controller,
|
||||
metricsAdapter: metricsAdapter,
|
||||
}
|
||||
}
|
||||
|
||||
// StartServer starts the metrics server
|
||||
func (m *MetricsServer) StartServer(stopCh <-chan struct{}) error {
|
||||
go m.metricsController.startController(stopCh)
|
||||
return m.metricsAdapter.Run(stopCh)
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package metricsadapter
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
clusterV1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
||||
informerfactory "github.com/karmada-io/karmada/pkg/generated/informers/externalversions"
|
||||
clusterlister "github.com/karmada-io/karmada/pkg/generated/listers/cluster/v1alpha1"
|
||||
"github.com/karmada-io/karmada/pkg/metricsadapter/provider"
|
||||
"github.com/karmada-io/karmada/pkg/util"
|
||||
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
||||
"github.com/karmada-io/karmada/pkg/util/gclient"
|
||||
)
|
||||
|
||||
// MetricsController is a controller for metrics, control the lifecycle of multi-clusters informer
|
||||
type MetricsController struct {
|
||||
InformerFactory informerfactory.SharedInformerFactory
|
||||
ClusterLister clusterlister.ClusterLister
|
||||
InformerManager genericmanager.MultiClusterInformerManager
|
||||
|
||||
queue workqueue.RateLimitingInterface
|
||||
restConfig *rest.Config
|
||||
}
|
||||
|
||||
// NewMetricsController creates a new metrics controller
|
||||
func NewMetricsController(restConfig *rest.Config, factory informerfactory.SharedInformerFactory) *MetricsController {
|
||||
clusterLister := factory.Cluster().V1alpha1().Clusters().Lister()
|
||||
controller := &MetricsController{
|
||||
InformerFactory: factory,
|
||||
ClusterLister: clusterLister,
|
||||
InformerManager: genericmanager.GetInstance(),
|
||||
restConfig: restConfig,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "metrics-adapter"),
|
||||
}
|
||||
controller.addEventHandler()
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
// addEventHandler adds event handler for cluster
|
||||
func (m *MetricsController) addEventHandler() {
|
||||
clusterInformer := m.InformerFactory.Cluster().V1alpha1().Clusters().Informer()
|
||||
_, err := clusterInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: m.addCluster,
|
||||
// Update event and delete event will be handled by the same handler
|
||||
UpdateFunc: m.updateCluster,
|
||||
})
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to add cluster event handler for cluster: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// addCluster adds cluster to queue
|
||||
func (m *MetricsController) addCluster(obj interface{}) {
|
||||
cluster := obj.(*clusterV1alpha1.Cluster)
|
||||
m.queue.Add(cluster.GetName())
|
||||
}
|
||||
|
||||
// updateCluster updates cluster in queue
|
||||
func (m *MetricsController) updateCluster(oldObj, curObj interface{}) {
|
||||
curCluster := curObj.(*clusterV1alpha1.Cluster)
|
||||
oldCluster := oldObj.(*clusterV1alpha1.Cluster)
|
||||
if curCluster.ResourceVersion == oldCluster.ResourceVersion {
|
||||
// no change, do nothing.
|
||||
return
|
||||
}
|
||||
|
||||
if oldCluster.DeletionTimestamp.IsZero() != curCluster.DeletionTimestamp.IsZero() {
|
||||
// cluster is being deleted.
|
||||
m.queue.Add(curCluster.GetName())
|
||||
}
|
||||
|
||||
if util.IsClusterSpecPartCriticalUpdated(curCluster.Spec, oldCluster.Spec) ||
|
||||
util.IsClusterReady(&curCluster.Status) != util.IsClusterReady(&oldCluster.Status) {
|
||||
// Cluster.Spec or Cluster health state is changed, rebuild informer.
|
||||
m.InformerManager.Stop(curCluster.GetName())
|
||||
m.queue.Add(curCluster.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
// startController starts controller
|
||||
func (m *MetricsController) startController(stopCh <-chan struct{}) {
|
||||
m.InformerFactory.WaitForCacheSync(stopCh)
|
||||
|
||||
go wait.Until(m.worker, time.Second, stopCh)
|
||||
|
||||
go func() {
|
||||
<-stopCh
|
||||
genericmanager.StopInstance()
|
||||
klog.Infof("Shutting down karmada-metrics-adapter")
|
||||
}()
|
||||
}
|
||||
|
||||
// worker is a worker for handle the data in queue
|
||||
func (m *MetricsController) worker() {
|
||||
for m.handleClusters() {
|
||||
}
|
||||
}
|
||||
|
||||
// handleClusters handles clusters changes
|
||||
func (m *MetricsController) handleClusters() bool {
|
||||
key, shutdown := m.queue.Get()
|
||||
if shutdown {
|
||||
klog.Errorf("Fail to pop item from queue")
|
||||
return false
|
||||
}
|
||||
defer m.queue.Done(key)
|
||||
|
||||
clusterName := key.(string)
|
||||
cls, err := m.ClusterLister.Get(clusterName)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
klog.Infof("try to stop cluster informer %s", clusterName)
|
||||
m.InformerManager.Stop(clusterName)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if !cls.DeletionTimestamp.IsZero() {
|
||||
klog.Infof("try to stop cluster informer %s", clusterName)
|
||||
m.InformerManager.Stop(clusterName)
|
||||
return true
|
||||
}
|
||||
|
||||
if !util.IsClusterReady(&cls.Status) {
|
||||
klog.Warningf("cluster %s is notReady try to stop this cluster informer", clusterName)
|
||||
m.InformerManager.Stop(clusterName)
|
||||
return false
|
||||
}
|
||||
|
||||
if !m.InformerManager.IsManagerExist(clusterName) {
|
||||
klog.Info("Try to build informer manager for cluster ", clusterName)
|
||||
controlPlaneClient := gclient.NewForConfigOrDie(m.restConfig)
|
||||
clusterDynamicClient, err := util.NewClusterDynamicClientSet(clusterName, controlPlaneClient)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_ = m.InformerManager.ForCluster(clusterName, clusterDynamicClient.DynamicClientSet, 0)
|
||||
}
|
||||
sci := m.InformerManager.GetSingleClusterManager(clusterName)
|
||||
|
||||
// Just trigger the informer to work
|
||||
_ = sci.Lister(provider.PodsGVR)
|
||||
_ = sci.Lister(provider.NodesGVR)
|
||||
|
||||
sci.Start()
|
||||
_ = sci.WaitForCacheSync()
|
||||
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/metrics/pkg/apis/custom_metrics"
|
||||
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider"
|
||||
)
|
||||
|
||||
// CustomMetricsProvider is a custom metrics provider
|
||||
type CustomMetricsProvider struct {
|
||||
}
|
||||
|
||||
// MakeCustomMetricsProvider creates a new custom metrics provider
|
||||
func MakeCustomMetricsProvider() *CustomMetricsProvider {
|
||||
return &CustomMetricsProvider{}
|
||||
}
|
||||
|
||||
// GetMetricByName will query metrics by name from member clusters and return the result
|
||||
func (c *CustomMetricsProvider) GetMetricByName(ctx context.Context, name types.NamespacedName, info provider.CustomMetricInfo, metricSelector labels.Selector) (*custom_metrics.MetricValue, error) {
|
||||
return nil, fmt.Errorf("karmada-metrics-adapter still not implement it")
|
||||
}
|
||||
|
||||
// GetMetricBySelector will query metrics by selector from member clusters and return the result
|
||||
func (c *CustomMetricsProvider) GetMetricBySelector(ctx context.Context, namespace string, selector labels.Selector, info provider.CustomMetricInfo, metricSelector labels.Selector) (*custom_metrics.MetricValueList, error) {
|
||||
return nil, fmt.Errorf("karmada-metrics-adapter still not implement it")
|
||||
}
|
||||
|
||||
// ListAllMetrics returns all metrics in all member clusters
|
||||
func (c *CustomMetricsProvider) ListAllMetrics() []provider.CustomMetricInfo {
|
||||
return []provider.CustomMetricInfo{}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/metrics/pkg/apis/external_metrics"
|
||||
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider"
|
||||
)
|
||||
|
||||
// ExternalMetricsProvider is a custom metrics provider
|
||||
type ExternalMetricsProvider struct {
|
||||
}
|
||||
|
||||
// MakeExternalMetricsProvider creates a new custom metrics provider
|
||||
func MakeExternalMetricsProvider() *ExternalMetricsProvider {
|
||||
return &ExternalMetricsProvider{}
|
||||
}
|
||||
|
||||
// GetExternalMetric will query metrics by selector from member clusters and return the result
|
||||
func (c *ExternalMetricsProvider) GetExternalMetric(ctx context.Context, namespace string, metricSelector labels.Selector, info provider.ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error) {
|
||||
return nil, fmt.Errorf("karmada-metrics-adapter still not implement it")
|
||||
}
|
||||
|
||||
// ListAllExternalMetrics returns all metrics in all member clusters
|
||||
func (c *ExternalMetricsProvider) ListAllExternalMetrics() []provider.ExternalMetricInfo {
|
||||
return []provider.ExternalMetricInfo{}
|
||||
}
|
|
@ -0,0 +1,462 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
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/runtime"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/metrics/pkg/apis/metrics"
|
||||
metricsv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
|
||||
clusterlister "github.com/karmada-io/karmada/pkg/generated/listers/cluster/v1alpha1"
|
||||
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
||||
"github.com/karmada-io/karmada/pkg/util/helper"
|
||||
)
|
||||
|
||||
const (
|
||||
// labelSelectorAnnotationInternal is the annotation used internal in karmada-metrics-adapter,
|
||||
// to record the selector specified by the user
|
||||
labelSelectorAnnotationInternal = "internal.karmada.io/selector"
|
||||
)
|
||||
|
||||
var (
|
||||
// podMetricsGVR is the gvr of pod metrics(v1beta1 version)
|
||||
podMetricsGVR = metricsv1beta1.SchemeGroupVersion.WithResource("pods")
|
||||
// nodeMetricsGVR is the gvr of node metrics(v1beta1 version)
|
||||
nodeMetricsGVR = metricsv1beta1.SchemeGroupVersion.WithResource("nodes")
|
||||
// PodsGVR is the gvr of pods
|
||||
PodsGVR = corev1.SchemeGroupVersion.WithResource("pods")
|
||||
// NodesGVR is the gvr of nodes
|
||||
NodesGVR = corev1.SchemeGroupVersion.WithResource("nodes")
|
||||
)
|
||||
|
||||
type queryResourceFromClustersFunc func(sci genericmanager.SingleClusterInformerManager, clusterName string) error
|
||||
type queryMetricsFromClustersFunc func(sci genericmanager.SingleClusterInformerManager, clusterName string) (interface{}, error)
|
||||
|
||||
// ResourceMetricsProvider is a resource metrics provider, to provide cpu/memory metrics
|
||||
type ResourceMetricsProvider struct {
|
||||
PodLister *PodLister
|
||||
NodeLister *NodeLister
|
||||
|
||||
clusterLister clusterlister.ClusterLister
|
||||
informerManager genericmanager.MultiClusterInformerManager
|
||||
}
|
||||
|
||||
// NewResourceMetricsProvider creates a new resource metrics provider
|
||||
func NewResourceMetricsProvider(clusterLister clusterlister.ClusterLister, informerManager genericmanager.MultiClusterInformerManager) *ResourceMetricsProvider {
|
||||
return &ResourceMetricsProvider{
|
||||
clusterLister: clusterLister,
|
||||
informerManager: informerManager,
|
||||
PodLister: NewPodLister(),
|
||||
NodeLister: NewNodeLister(),
|
||||
}
|
||||
}
|
||||
|
||||
// getMetricsParallel is a parallel func of to query metrics from member clusters
|
||||
func (r *ResourceMetricsProvider) getMetricsParallel(resourceFunc queryResourceFromClustersFunc,
|
||||
metricsFunc queryMetricsFromClustersFunc) ([]interface{}, error) {
|
||||
clusters, err := r.clusterLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to list clusters: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// step 1. Find out the target clusters with lister cache
|
||||
var targetClusters []string
|
||||
for _, cluster := range clusters {
|
||||
sci := r.informerManager.GetSingleClusterManager(cluster.Name)
|
||||
if sci == nil {
|
||||
klog.Errorf("Failed to get cluster(%s) manager", cluster.Name)
|
||||
continue
|
||||
}
|
||||
err := resourceFunc(sci, cluster.Name)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
klog.Errorf("Failed to query resource in cluster(%s): %v", cluster.Name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
targetClusters = append(targetClusters, cluster.Name)
|
||||
}
|
||||
|
||||
var metrics []interface{}
|
||||
if len(targetClusters) == 0 {
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// step 2. Query metrics from the filtered target clusters
|
||||
metricsChanel := make(chan interface{})
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, clusterName := range targetClusters {
|
||||
wg.Add(1)
|
||||
|
||||
go func(cluster string) {
|
||||
defer wg.Done()
|
||||
|
||||
sci := r.informerManager.GetSingleClusterManager(cluster)
|
||||
if sci == nil {
|
||||
klog.Errorf("Failed to get cluster(%s) manager", cluster)
|
||||
return
|
||||
}
|
||||
|
||||
metrics, err := metricsFunc(sci, cluster)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
klog.Errorf("Failed to query metrics in cluster(%s): %v", cluster, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// If there are multiple metrics with same name, it's ok because it's an array instead of a map.
|
||||
// The HPA controller will calculate the average utilization with the array.
|
||||
metricsChanel <- metrics
|
||||
}(clusterName)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(metricsChanel)
|
||||
}()
|
||||
|
||||
for {
|
||||
data, ok := <-metricsChanel
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
metrics = append(metrics, data)
|
||||
}
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// queryPodMetricsByName queries metrics by pod name from target clusters
|
||||
func (r *ResourceMetricsProvider) queryPodMetricsByName(name, namespace string) ([]metrics.PodMetrics, error) {
|
||||
resourceQueryFunc := func(sci genericmanager.SingleClusterInformerManager, _ string) error {
|
||||
_, err := sci.Lister(PodsGVR).ByNamespace(namespace).Get(name)
|
||||
return err
|
||||
}
|
||||
metricsQueryFunc := func(sci genericmanager.SingleClusterInformerManager, _ string) (interface{}, error) {
|
||||
metrics, err := sci.GetClient().Resource(podMetricsGVR).
|
||||
Namespace(namespace).Get(context.Background(), name, metav1.GetOptions{})
|
||||
return metrics, err
|
||||
}
|
||||
|
||||
metricsQuery, err := r.getMetricsParallel(resourceQueryFunc, metricsQueryFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var podMetrics []metrics.PodMetrics
|
||||
for index := range metricsQuery {
|
||||
internalMetrics, err := metricsConvertV1beta1PodToInternalPod(*metricsQuery[index].(*unstructured.Unstructured))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
podMetrics = append(podMetrics, internalMetrics...)
|
||||
}
|
||||
|
||||
return podMetrics, nil
|
||||
}
|
||||
|
||||
// queryPodMetricsBySelector queries metrics by pod selector from target clusters
|
||||
func (r *ResourceMetricsProvider) queryPodMetricsBySelector(selector, namespace string) ([]metrics.PodMetrics, error) {
|
||||
labelSelector, err := labels.Parse(selector)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to parse label selector: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceQueryFunc := func(sci genericmanager.SingleClusterInformerManager, clusterName string) error {
|
||||
pods, err := sci.Lister(PodsGVR).ByNamespace(namespace).List(labelSelector)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to list pods in cluster(%s): %v", clusterName, err)
|
||||
return err
|
||||
}
|
||||
if len(pods) == 0 {
|
||||
return errors.NewNotFound(PodsGVR.GroupResource(), "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
metricsQueryFunc := func(sci genericmanager.SingleClusterInformerManager, _ string) (interface{}, error) {
|
||||
metrics, err := sci.GetClient().Resource(podMetricsGVR).
|
||||
Namespace(namespace).List(context.Background(), metav1.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
return metrics, err
|
||||
}
|
||||
|
||||
metricsQuery, err := r.getMetricsParallel(resourceQueryFunc, metricsQueryFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var podMetrics []metrics.PodMetrics
|
||||
for index := range metricsQuery {
|
||||
metricsData := metricsQuery[index].(*unstructured.UnstructuredList)
|
||||
internalMetrics, err := metricsConvertV1beta1PodToInternalPod(metricsData.Items...)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
podMetrics = append(podMetrics, internalMetrics...)
|
||||
}
|
||||
|
||||
return podMetrics, nil
|
||||
}
|
||||
|
||||
// queryNodeMetricsByName queries metrics by node name from target clusters
|
||||
func (r *ResourceMetricsProvider) queryNodeMetricsByName(name string) ([]metrics.NodeMetrics, error) {
|
||||
resourceQueryFunc := func(sci genericmanager.SingleClusterInformerManager, _ string) error {
|
||||
_, err := sci.Lister(NodesGVR).Get(name)
|
||||
return err
|
||||
}
|
||||
metricsQueryFunc := func(sci genericmanager.SingleClusterInformerManager, _ string) (interface{}, error) {
|
||||
metrics, err := sci.GetClient().Resource(nodeMetricsGVR).Get(context.Background(), name, metav1.GetOptions{})
|
||||
return metrics, err
|
||||
}
|
||||
|
||||
metricsQuery, err := r.getMetricsParallel(resourceQueryFunc, metricsQueryFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nodeMetrics []metrics.NodeMetrics
|
||||
for index := range metricsQuery {
|
||||
internalMetrics, err := metricsConvertV1beta1NodeToInternalNode(*metricsQuery[index].(*unstructured.Unstructured))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
nodeMetrics = append(nodeMetrics, internalMetrics...)
|
||||
}
|
||||
|
||||
return nodeMetrics, nil
|
||||
}
|
||||
|
||||
// queryNodeMetricsBySelector queries metrics by node selector from target clusters
|
||||
func (r *ResourceMetricsProvider) queryNodeMetricsBySelector(selector string) ([]metrics.NodeMetrics, error) {
|
||||
labelSelector, err := labels.Parse(selector)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to parse label selector: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceQueryFunc := func(sci genericmanager.SingleClusterInformerManager, clusterName string) error {
|
||||
nodes, err := sci.Lister(NodesGVR).List(labelSelector)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to list pods in cluster(%s): %v", clusterName, err)
|
||||
return err
|
||||
}
|
||||
if len(nodes) == 0 {
|
||||
return errors.NewNotFound(PodsGVR.GroupResource(), "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
metricsQueryFunc := func(sci genericmanager.SingleClusterInformerManager, _ string) (interface{}, error) {
|
||||
metrics, err := sci.GetClient().Resource(nodeMetricsGVR).List(context.Background(), metav1.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
return metrics, err
|
||||
}
|
||||
|
||||
metricsQuery, err := r.getMetricsParallel(resourceQueryFunc, metricsQueryFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nodeMetrics []metrics.NodeMetrics
|
||||
for index := range metricsQuery {
|
||||
metricsData := metricsQuery[index].(*unstructured.UnstructuredList)
|
||||
internalMetrics, err := metricsConvertV1beta1NodeToInternalNode(metricsData.Items...)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
nodeMetrics = append(nodeMetrics, internalMetrics...)
|
||||
}
|
||||
|
||||
return nodeMetrics, nil
|
||||
}
|
||||
|
||||
// GetPodMetrics queries metrics by the internal constructed pod
|
||||
func (r *ResourceMetricsProvider) GetPodMetrics(pods ...*metav1.PartialObjectMetadata) ([]metrics.PodMetrics, error) {
|
||||
var podMetrics []metrics.PodMetrics
|
||||
|
||||
// In the previous step, we construct pods list with only one element.
|
||||
if len(pods) != 1 {
|
||||
return podMetrics, nil
|
||||
}
|
||||
|
||||
// In the previous step, if query with label selector, the name will be set to empty
|
||||
if pods[0].Name == "" {
|
||||
namespace := pods[0].Namespace
|
||||
selectorStr := pods[0].Annotations[labelSelectorAnnotationInternal]
|
||||
return r.queryPodMetricsBySelector(selectorStr, namespace)
|
||||
}
|
||||
|
||||
return r.queryPodMetricsByName(pods[0].Name, pods[0].Namespace)
|
||||
}
|
||||
|
||||
// GetNodeMetrics queries metrics by the internal constructed node
|
||||
func (r *ResourceMetricsProvider) GetNodeMetrics(nodes ...*corev1.Node) ([]metrics.NodeMetrics, error) {
|
||||
var nodeMetrics []metrics.NodeMetrics
|
||||
|
||||
// In the previous step, we construct node list with only one element, this should never happen
|
||||
if len(nodes) != 1 {
|
||||
// never reach here
|
||||
return nodeMetrics, nil
|
||||
}
|
||||
|
||||
// In the previous step, if query with label selector, the name will be set to empty
|
||||
if nodes[0].Name == "" {
|
||||
selectorStr := nodes[0].Annotations[labelSelectorAnnotationInternal]
|
||||
return r.queryNodeMetricsBySelector(selectorStr)
|
||||
}
|
||||
|
||||
return r.queryNodeMetricsByName(nodes[0].Name)
|
||||
}
|
||||
|
||||
// PodLister is an internal lister for pods
|
||||
type PodLister struct {
|
||||
namespaceSpecified string
|
||||
}
|
||||
|
||||
// NewPodLister creates an internal new PodLister
|
||||
func NewPodLister() *PodLister {
|
||||
return &PodLister{}
|
||||
}
|
||||
|
||||
// List returns the internal constructed pod with label selector info
|
||||
func (p *PodLister) List(selector labels.Selector) (ret []runtime.Object, err error) {
|
||||
klog.V(4).Infof("List query pods with selector: %v", selector.String())
|
||||
|
||||
podData := &v1.PartialObjectMetadata{
|
||||
TypeMeta: v1.TypeMeta{},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: p.namespaceSpecified,
|
||||
Annotations: map[string]string{
|
||||
labelSelectorAnnotationInternal: selector.String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return []runtime.Object{podData}, nil
|
||||
}
|
||||
|
||||
// Get returns the internal constructed pod with name info
|
||||
func (p *PodLister) Get(name string) (runtime.Object, error) {
|
||||
klog.V(4).Infof("Query pod in namespace(%s) with name:%s", p.namespaceSpecified, name)
|
||||
|
||||
podData := &v1.PartialObjectMetadata{
|
||||
TypeMeta: v1.TypeMeta{},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: p.namespaceSpecified,
|
||||
},
|
||||
}
|
||||
return podData, nil
|
||||
}
|
||||
|
||||
// ByNamespace returns the pod lister with namespace info
|
||||
func (p *PodLister) ByNamespace(namespace string) cache.GenericNamespaceLister {
|
||||
klog.V(4).Infof("Query Pods in namespace: %s", namespace)
|
||||
|
||||
listerCopy := &PodLister{}
|
||||
listerCopy.namespaceSpecified = namespace
|
||||
return listerCopy
|
||||
}
|
||||
|
||||
// NodeLister is an internal lister for nodes
|
||||
type NodeLister struct {
|
||||
}
|
||||
|
||||
// NewNodeLister creates an internal new NodeLister
|
||||
func NewNodeLister() *NodeLister {
|
||||
return &NodeLister{}
|
||||
}
|
||||
|
||||
// List returns the internal constructed node with label selector info
|
||||
func (n *NodeLister) List(selector labels.Selector) (ret []*corev1.Node, err error) {
|
||||
klog.V(4).Infof("Query node metrics with selector: %s", selector.String())
|
||||
|
||||
node := &corev1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
labelSelectorAnnotationInternal: selector.String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
return []*corev1.Node{node}, nil
|
||||
}
|
||||
|
||||
// Get returns the internal constructed node with name info
|
||||
func (n *NodeLister) Get(name string) (*corev1.Node, error) {
|
||||
klog.V(4).Infof("Query node metrics with name:%s", name)
|
||||
|
||||
node := &corev1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// metricsConvertV1beta1PodToInternalPod converts metricsv1beta1.PodMetrics to metrics.PodMetrics
|
||||
func metricsConvertV1beta1PodToInternalPod(objs ...unstructured.Unstructured) ([]metrics.PodMetrics, error) {
|
||||
var podMetricsV1beta1 []metricsv1beta1.PodMetrics
|
||||
|
||||
for index := range objs {
|
||||
single := metricsv1beta1.PodMetrics{}
|
||||
if err := helper.ConvertToTypedObject(&objs[index], &single); err != nil {
|
||||
klog.Errorf("Failed to convert to typed object: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
podMetricsV1beta1 = append(podMetricsV1beta1, single)
|
||||
}
|
||||
|
||||
var podMetricsInternal []metrics.PodMetrics
|
||||
for index := range podMetricsV1beta1 {
|
||||
single := metrics.PodMetrics{}
|
||||
if err := metricsv1beta1.Convert_v1beta1_PodMetrics_To_metrics_PodMetrics(&podMetricsV1beta1[index], &single, nil); err != nil {
|
||||
klog.Errorf("Failed to convert to typed object: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podMetricsInternal = append(podMetricsInternal, single)
|
||||
}
|
||||
|
||||
return podMetricsInternal, nil
|
||||
}
|
||||
|
||||
// metricsConvertV1beta1NodeToInternalNode converts metricsv1beta1.NodeMetrics to metrics.NodeMetrics
|
||||
func metricsConvertV1beta1NodeToInternalNode(objs ...unstructured.Unstructured) ([]metrics.NodeMetrics, error) {
|
||||
var nodeMetricsV1beta1 []metricsv1beta1.NodeMetrics
|
||||
|
||||
for index := range objs {
|
||||
single := metricsv1beta1.NodeMetrics{}
|
||||
if err := helper.ConvertToTypedObject(&objs[index], &single); err != nil {
|
||||
klog.Errorf("Failed to convert to typed object: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
nodeMetricsV1beta1 = append(nodeMetricsV1beta1, single)
|
||||
}
|
||||
|
||||
var nodeMetricsInternal []metrics.NodeMetrics
|
||||
for index := range nodeMetricsV1beta1 {
|
||||
single := metrics.NodeMetrics{}
|
||||
if err := metricsv1beta1.Convert_v1beta1_NodeMetrics_To_metrics_NodeMetrics(&nodeMetricsV1beta1[index], &single, nil); err != nil {
|
||||
klog.Errorf("Failed to convert to typed object: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeMetricsInternal = append(nodeMetricsInternal, single)
|
||||
}
|
||||
|
||||
return nodeMetricsInternal, nil
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"reflect"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -190,3 +191,14 @@ func IsClusterIdentifyUnique(controlPlaneClient karmadaclientset.Interface, id s
|
|||
}
|
||||
return true, "", nil
|
||||
}
|
||||
|
||||
// IsClusterSpecPartCriticalUpdated checks whether the critical part of ClusterSpec has been updated
|
||||
func IsClusterSpecPartCriticalUpdated(newSpec, oldSpec clusterv1alpha1.ClusterSpec) bool {
|
||||
if oldSpec.APIEndpoint == newSpec.APIEndpoint &&
|
||||
oldSpec.InsecureSkipTLSVerification == newSpec.InsecureSkipTLSVerification &&
|
||||
oldSpec.ProxyURL == newSpec.ProxyURL &&
|
||||
equality.Semantic.DeepEqual(oldSpec.ProxyHeader, newSpec.ProxyHeader) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue