diff --git a/go.mod b/go.mod index 4db4c1c04de8..5c8630d9f301 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( k8s.io/apiextensions-apiserver v0.24.1 k8s.io/apimachinery v0.24.1 k8s.io/client-go v0.24.1 - k8s.io/heapster v1.5.4 ) require ( diff --git a/go.sum b/go.sum index 90eccf61804e..343a46ceda61 100644 --- a/go.sum +++ b/go.sum @@ -956,8 +956,6 @@ k8s.io/code-generator v0.24.1/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI k8s.io/component-base v0.24.1/go.mod h1:DW5vQGYVCog8WYpNob3PMmmsY8A3L9QZNg4j/dV3s38= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/heapster v1.5.4 h1:lH2GCZdqRmUKDoyqRgiXbRmIcevaPYTvkguOuYUl8gQ= -k8s.io/heapster v1.5.4/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= 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.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= diff --git a/src/app/backend/dashboard.go b/src/app/backend/dashboard.go index a9c926f31102..42c3a92ce8df 100644 --- a/src/app/backend/dashboard.go +++ b/src/app/backend/dashboard.go @@ -37,6 +37,7 @@ import ( "github.com/kubernetes/dashboard/src/app/backend/client" clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api" "github.com/kubernetes/dashboard/src/app/backend/handler" + "github.com/kubernetes/dashboard/src/app/backend/health" "github.com/kubernetes/dashboard/src/app/backend/integration" integrationapi "github.com/kubernetes/dashboard/src/app/backend/integration/api" "github.com/kubernetes/dashboard/src/app/backend/settings" @@ -110,6 +111,9 @@ func main() { systemBannerManager := systembanner.NewSystemBannerManager(args.Holder.GetSystemBanner(), args.Holder.GetSystemBannerSeverity()) + // Init health manager + healthManager := health.NewHealthManager(clientManager) + // Init integrations integrationManager := integration.NewIntegrationManager(clientManager) @@ -117,9 +121,6 @@ func main() { case "sidecar": integrationManager.Metric().ConfigureSidecar(args.Holder.GetSidecarHost()). EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.Holder.GetMetricClientCheckPeriod())) - case "heapster": - integrationManager.Metric().ConfigureHeapster(args.Holder.GetHeapsterHost()). - EnableWithRetry(integrationapi.HeapsterIntegrationID, time.Duration(args.Holder.GetMetricClientCheckPeriod())) case "none": log.Print("no metrics provider selected, will not check metrics.") default: @@ -139,6 +140,8 @@ func main() { handleFatalInitError(err) } + healthHandler := health.NewHealthHandler(healthManager) + var servingCerts []tls.Certificate if args.Holder.GetAutoGenerateCertificates() { log.Println("Auto-generating certificates") @@ -161,6 +164,7 @@ func main() { // Run a HTTP server that serves static public files from './public' and handles API calls. http.Handle("/", handler.MakeGzipHandler(handler.CreateLocaleHandler())) + http.Handle("/health", handler.AppHandler(healthHandler.Install)) http.Handle("/api/", apiHandler) http.Handle("/config", handler.AppHandler(handler.ConfigHandler)) http.Handle("/api/sockjs/", handler.CreateAttachHandler("/api/sockjs")) diff --git a/src/app/backend/handler/apihandler.go b/src/app/backend/handler/apihandler.go index f320273e7729..281f1c19a2ce 100644 --- a/src/app/backend/handler/apihandler.go +++ b/src/app/backend/handler/apihandler.go @@ -20,17 +20,9 @@ import ( "strconv" "strings" - v1 "k8s.io/api/core/v1" - - "github.com/kubernetes/dashboard/src/app/backend/resource/networkpolicy" - - "github.com/kubernetes/dashboard/src/app/backend/handler/parser" - "github.com/kubernetes/dashboard/src/app/backend/resource/customresourcedefinition/types" - - "github.com/kubernetes/dashboard/src/app/backend/plugin" - "github.com/emicklei/go-restful/v3" "golang.org/x/net/xsrftoken" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/remotecommand" @@ -39,7 +31,9 @@ import ( authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api" clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api" "github.com/kubernetes/dashboard/src/app/backend/errors" + "github.com/kubernetes/dashboard/src/app/backend/handler/parser" "github.com/kubernetes/dashboard/src/app/backend/integration" + "github.com/kubernetes/dashboard/src/app/backend/plugin" "github.com/kubernetes/dashboard/src/app/backend/resource/clusterrole" "github.com/kubernetes/dashboard/src/app/backend/resource/clusterrolebinding" "github.com/kubernetes/dashboard/src/app/backend/resource/common" @@ -48,6 +42,7 @@ import ( "github.com/kubernetes/dashboard/src/app/backend/resource/controller" "github.com/kubernetes/dashboard/src/app/backend/resource/cronjob" "github.com/kubernetes/dashboard/src/app/backend/resource/customresourcedefinition" + "github.com/kubernetes/dashboard/src/app/backend/resource/customresourcedefinition/types" "github.com/kubernetes/dashboard/src/app/backend/resource/daemonset" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" "github.com/kubernetes/dashboard/src/app/backend/resource/deployment" @@ -58,6 +53,7 @@ import ( "github.com/kubernetes/dashboard/src/app/backend/resource/job" "github.com/kubernetes/dashboard/src/app/backend/resource/logs" ns "github.com/kubernetes/dashboard/src/app/backend/resource/namespace" + "github.com/kubernetes/dashboard/src/app/backend/resource/networkpolicy" "github.com/kubernetes/dashboard/src/app/backend/resource/node" "github.com/kubernetes/dashboard/src/app/backend/resource/persistentvolume" "github.com/kubernetes/dashboard/src/app/backend/resource/persistentvolumeclaim" diff --git a/src/app/backend/health/api/types.go b/src/app/backend/health/api/types.go new file mode 100644 index 000000000000..b514aadb128a --- /dev/null +++ b/src/app/backend/health/api/types.go @@ -0,0 +1,19 @@ +// 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 api + +type Health struct { + Running bool `json:"running"` +} diff --git a/src/app/backend/health/handler.go b/src/app/backend/health/handler.go new file mode 100644 index 000000000000..98852cb75fed --- /dev/null +++ b/src/app/backend/health/handler.go @@ -0,0 +1,36 @@ +// 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 health + +import ( + "encoding/json" + "net/http" +) + +// HealthHandler manages all endpoints related to system banner management. +type HealthHandler struct { + manager HealthManager +} + +// Install creates new endpoints for system banner management. +func (self *HealthHandler) Install(w http.ResponseWriter, _ *http.Request) (int, error) { + w.Header().Set("Content-Type", "application/json") + return http.StatusOK, json.NewEncoder(w).Encode(self.manager.Get()) +} + +// NewHealthHandler creates HealthHandler. +func NewHealthHandler(manager HealthManager) HealthHandler { + return HealthHandler{manager: manager} +} diff --git a/src/app/backend/health/manager.go b/src/app/backend/health/manager.go new file mode 100644 index 000000000000..e353ab90d7ce --- /dev/null +++ b/src/app/backend/health/manager.go @@ -0,0 +1,40 @@ +// 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 health + +import ( + client "github.com/kubernetes/dashboard/src/app/backend/client/api" + health "github.com/kubernetes/dashboard/src/app/backend/health/api" +) + +// HealthManager is a structure containing all system banner manager members. +type HealthManager struct { + client client.ClientManager +} + +// NewHealthManager creates new settings manager. +func NewHealthManager(client client.ClientManager) HealthManager { + return HealthManager{ + client: client, + } +} + +// Get implements HealthManager interface. Check it for more information. +func (sbm *HealthManager) Get() health.Health { + _, err := sbm.client.InsecureClient().Discovery().ServerVersion() + return health.Health{ + Running: err == nil, + } +} diff --git a/src/app/backend/integration/api/types.go b/src/app/backend/integration/api/types.go index 289f54954ad3..727859b46145 100644 --- a/src/app/backend/integration/api/types.go +++ b/src/app/backend/integration/api/types.go @@ -22,8 +22,7 @@ type IntegrationID string // Integration app IDs should be registered in this block. const ( - HeapsterIntegrationID IntegrationID = "heapster" - SidecarIntegrationID IntegrationID = "sidecar" + SidecarIntegrationID IntegrationID = "sidecar" ) // Integration represents application integrated into the dashboard. Every application diff --git a/src/app/backend/integration/manager_test.go b/src/app/backend/integration/manager_test.go index fc0cfb64bc98..f79a567ff0dd 100644 --- a/src/app/backend/integration/manager_test.go +++ b/src/app/backend/integration/manager_test.go @@ -45,19 +45,12 @@ func TestIntegrationManager_GetState(t *testing.T) { cases := []struct { info string apiServerHost string - heapsterHost string + sidecarHost string expected *api.IntegrationState expectedErr error }{ { - "Server provided and using in-cluster heapster", - "http://127.0.0.1:8080", "", &api.IntegrationState{ - Connected: false, - Error: errors.NewInvalid("Get http://127.0.0.1:8080/api/v1/namespaces/kube-system/services/heapster/proxy/healthz: dial tcp 127.0.0.1:8080: connect: connection refused"), - }, nil, - }, - { - "Server provided and using external heapster", + "Server provided and using external sidecar", "http://127.0.0.1:8080", "http://127.0.0.1:8081", &api.IntegrationState{ Connected: false, Error: errors.NewInvalid("Get http://127.0.0.1:8081/healthz: dial tcp 127.0.0.1:8081: connect: connection refused"), @@ -68,9 +61,9 @@ func TestIntegrationManager_GetState(t *testing.T) { for _, c := range cases { cManager := client.NewClientManager("", c.apiServerHost) iManager := NewIntegrationManager(cManager) - iManager.Metric().ConfigureHeapster(c.heapsterHost) + iManager.Metric().ConfigureSidecar(c.sidecarHost) - state, err := iManager.GetState(api.HeapsterIntegrationID) + state, err := iManager.GetState(api.SidecarIntegrationID) if !areErrorsEqual(err, c.expectedErr) { t.Errorf("Test Case: %s. Expected error to be: %v, but got %v.", c.info, c.expectedErr, err) diff --git a/src/app/backend/integration/metric/heapster/client.go b/src/app/backend/integration/metric/heapster/client.go deleted file mode 100644 index cbe60324d501..000000000000 --- a/src/app/backend/integration/metric/heapster/client.go +++ /dev/null @@ -1,255 +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 heapster - -import ( - "context" - "encoding/json" - "fmt" - "log" - "strings" - - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - heapster "k8s.io/heapster/metrics/api/v1/types" - - "github.com/kubernetes/dashboard/src/app/backend/client" - "github.com/kubernetes/dashboard/src/app/backend/errors" - integrationapi "github.com/kubernetes/dashboard/src/app/backend/integration/api" - metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" - "github.com/kubernetes/dashboard/src/app/backend/integration/metric/common" -) - -// Heapster client implements MetricClient and Integration interfaces. -type heapsterClient struct { - client HeapsterRESTClient -} - -// Implement Integration interface. - -// HealthCheck implements integration app interface. See Integration interface for more information. -func (self heapsterClient) HealthCheck() error { - if self.client == nil { - return errors.NewInvalid("Heapster not configured") - } - - return self.client.HealthCheck() -} - -// ID implements integration app interface. See Integration interface for more information. -func (self heapsterClient) ID() integrationapi.IntegrationID { - return integrationapi.HeapsterIntegrationID -} - -// Implement MetricClient interface - -// DownloadMetrics implements metric client interface. See MetricClient for more information. -func (self heapsterClient) DownloadMetrics(selectors []metricapi.ResourceSelector, - metricNames []string, cachedResources *metricapi.CachedResources) metricapi.MetricPromises { - result := metricapi.MetricPromises{} - for _, metricName := range metricNames { - collectedMetrics := self.DownloadMetric(selectors, metricName, cachedResources) - result = append(result, collectedMetrics...) - } - return result -} - -// DownloadMetric implements metric client interface. See MetricClient for more information. -func (self heapsterClient) DownloadMetric(selectors []metricapi.ResourceSelector, - metricName string, cachedResources *metricapi.CachedResources) metricapi.MetricPromises { - heapsterSelectors := getHeapsterSelectors(selectors, cachedResources) - - // Downloads metric in the fastest possible way by first compressing HeapsterSelectors and later unpacking the result to separate boxes. - compressedSelectors, reverseMapping := compress(heapsterSelectors) - return self.downloadMetric(heapsterSelectors, compressedSelectors, reverseMapping, metricName) -} - -// AggregateMetrics implements metric client interface. See MetricClient for more information. -func (self heapsterClient) AggregateMetrics(metrics metricapi.MetricPromises, metricName string, - aggregations metricapi.AggregationModes) metricapi.MetricPromises { - return common.AggregateMetricPromises(metrics, metricName, aggregations, nil) -} - -func (self heapsterClient) downloadMetric(heapsterSelectors []heapsterSelector, - compressedSelectors []heapsterSelector, reverseMapping map[string][]int, - metricName string) metricapi.MetricPromises { - // collect all the required data (as promises) - unassignedResourcePromisesList := make([]metricapi.MetricPromises, len(compressedSelectors)) - for selectorId, compressedSelector := range compressedSelectors { - unassignedResourcePromisesList[selectorId] = - self.downloadMetricForEachTargetResource(compressedSelector, metricName) - } - // prepare final result - result := metricapi.NewMetricPromises(len(heapsterSelectors)) - // unpack downloaded data - this is threading safe because there is only one thread running. - - // unpack the data selector by selector. - for selectorId, selector := range compressedSelectors { - unassignedResourcePromises := unassignedResourcePromisesList[selectorId] - // now unpack the resources and push errors in case of error. - unassignedResources, err := unassignedResourcePromises.GetMetrics() - if err != nil { - for _, originalMappingIndex := range reverseMapping[selector.Path] { - result[originalMappingIndex].Error <- err - result[originalMappingIndex].Metric <- nil - } - continue - } - unassignedResourceMap := map[types.UID]metricapi.Metric{} - for _, unassignedMetric := range unassignedResources { - unassignedResourceMap[unassignedMetric. - Label[selector.TargetResourceType][0]] = unassignedMetric - } - - // now, if everything went ok, unpack the metrics into original selectors - for _, originalMappingIndex := range reverseMapping[selector.Path] { - // find out what resources this selector needs - requestedResources := []metricapi.Metric{} - for _, requestedResourceUID := range heapsterSelectors[originalMappingIndex]. - Label[selector.TargetResourceType] { - requestedResources = append(requestedResources, - unassignedResourceMap[requestedResourceUID]) - } - - // aggregate the data for this resource - aggregatedMetric := common.AggregateData(requestedResources, metricName, metricapi.SumAggregation) - result[originalMappingIndex].Metric <- &aggregatedMetric - result[originalMappingIndex].Error <- nil - } - } - - return result -} - -// downloadMetricForEachTargetResource downloads requested metric for each resource present in HeapsterSelector -// and returns the result as a list of promises - one promise for each resource. Order of promises returned is the same as order in self.Resources. -func (self heapsterClient) downloadMetricForEachTargetResource(selector heapsterSelector, metricName string) metricapi.MetricPromises { - var notAggregatedMetrics metricapi.MetricPromises - if HeapsterAllInOneDownloadConfig[selector.TargetResourceType] { - notAggregatedMetrics = self.allInOneDownload(selector, metricName) - } else { - notAggregatedMetrics = metricapi.MetricPromises{} - for i := range selector.Resources { - notAggregatedMetrics = append(notAggregatedMetrics, self.ithResourceDownload(selector, metricName, i)) - } - } - return notAggregatedMetrics -} - -// ithResourceDownload downloads metric for ith resource in self.Resources. Use only in case all in 1 download is not supported -// for this resource type. -func (self heapsterClient) ithResourceDownload(selector heapsterSelector, metricName string, - i int) metricapi.MetricPromise { - result := metricapi.NewMetricPromise() - go func() { - rawResult := heapster.MetricResult{} - err := self.unmarshalType(selector.Path+selector.Resources[i]+"/metrics/"+metricName, &rawResult) - if err != nil { - result.Metric <- nil - result.Error <- err - return - } - dataPoints := DataPointsFromMetricJSONFormat(rawResult) - - result.Metric <- &metricapi.Metric{ - DataPoints: dataPoints, - MetricPoints: toMetricPoints(rawResult.Metrics), - MetricName: metricName, - Label: metricapi.Label{ - selector.TargetResourceType: []types.UID{ - selector.Label[selector.TargetResourceType][i], - }, - }, - } - result.Error <- nil - return - }() - return result -} - -// allInOneDownload downloads metrics for all resources present in self.Resources in one request. -// returns a list of metric promises - one promise for each resource. Order of self.Resources is preserved. -func (self heapsterClient) allInOneDownload(selector heapsterSelector, metricName string) metricapi.MetricPromises { - result := metricapi.NewMetricPromises(len(selector.Resources)) - go func() { - if len(selector.Resources) == 0 { - return - } - rawResults := heapster.MetricResultList{} - err := self.unmarshalType(selector.Path+strings.Join(selector.Resources, ",")+"/metrics/"+metricName, &rawResults) - if err != nil { - result.PutMetrics(nil, err) - return - } - if len(result) != len(rawResults.Items) { - result.PutMetrics(nil, fmt.Errorf(`Received invalid number of resources from heapster. Expected %d received %d`, len(result), len(rawResults.Items))) - return - } - - for i, rawResult := range rawResults.Items { - dataPoints := DataPointsFromMetricJSONFormat(rawResult) - - result[i].Metric <- &metricapi.Metric{ - DataPoints: dataPoints, - MetricPoints: toMetricPoints(rawResult.Metrics), - MetricName: metricName, - Label: metricapi.Label{ - selector.TargetResourceType: []types.UID{ - selector.Label[selector.TargetResourceType][i], - }, - }, - } - result[i].Error <- nil - } - return - - }() - return result -} - -// unmarshalType performs heapster GET request to the specifies path and transfers -// the data to the interface provided. -func (self heapsterClient) unmarshalType(path string, v interface{}) error { - rawData, err := self.client.Get("/model/" + path).DoRaw(context.TODO()) - if err != nil { - return err - } - return json.Unmarshal(rawData, v) -} - -// CreateHeapsterClient creates new Heapster client. When heapsterHost param is empty -// string the function assumes that it is running inside a Kubernetes cluster and connects via -// service proxy. heapsterHost param is in the format of protocol://address:port, -// e.g., http://localhost:8002. -func CreateHeapsterClient(host string, k8sClient kubernetes.Interface) ( - metricapi.MetricClient, error) { - - if host == "" && k8sClient != nil { - log.Print("Creating in-cluster Heapster client") - c := inClusterHeapsterClient{client: k8sClient.CoreV1().RESTClient()} - return heapsterClient{client: c}, nil - } - - cfg := &rest.Config{Host: host, QPS: client.DefaultQPS, Burst: client.DefaultBurst} - restClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - return heapsterClient{}, err - } - log.Printf("Creating remote Heapster client for %s", host) - c := remoteHeapsterClient{client: restClient.CoreV1().RESTClient()} - - return heapsterClient{client: c}, nil -} diff --git a/src/app/backend/integration/metric/heapster/client_test.go b/src/app/backend/integration/metric/heapster/client_test.go deleted file mode 100644 index 28b8cf08233c..000000000000 --- a/src/app/backend/integration/metric/heapster/client_test.go +++ /dev/null @@ -1,497 +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 heapster - -import ( - "context" - "encoding/json" - "fmt" - "log" - "reflect" - "regexp" - "strings" - "sync/atomic" - "testing" - "time" - - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - heapster "k8s.io/heapster/metrics/api/v1/types" - - "github.com/kubernetes/dashboard/src/app/backend/api" - "github.com/kubernetes/dashboard/src/app/backend/client" - "github.com/kubernetes/dashboard/src/app/backend/errors" - integrationapi "github.com/kubernetes/dashboard/src/app/backend/integration/api" - metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" -) - -func areErrorsEqual(err1, err2 error) bool { - return (err1 != nil && err2 != nil && normalize(err1.Error()) == normalize(err2.Error())) || - (err1 == nil && err2 == nil) -} - -// Removes all quote signs that might have been added to the message. -// Might depend on dependencies version how they are constructed. -func normalize(msg string) string { - return strings.Replace(msg, "\"", "", -1) -} - -type GlobalCounter int32 - -func (c *GlobalCounter) increment() int32 { - return atomic.AddInt32((*int32)(c), 1) -} - -func (c *GlobalCounter) get() int32 { - return atomic.LoadInt32((*int32)(c)) -} - -func (c *GlobalCounter) set(val int32) { - atomic.StoreInt32((*int32)(c), val) -} - -var _NumRequests = GlobalCounter(0) - -type FakeHeapster struct { - PodData - NodeData -} - -type FakeRequest struct { - PodData - NodeData - Path string -} - -type PodData map[string][]heapster.MetricPoint -type NodeData map[string][]heapster.MetricPoint - -func (self FakeHeapster) Get(path string) RequestInterface { - return FakeRequest{self.PodData, self.NodeData, path} -} - -func (self FakeHeapster) GetNumberOfRequestsMade() int { - num := int(_NumRequests.get()) - _NumRequests.set(0) - return num -} - -func (self FakeHeapster) HealthCheck() error { - return nil -} - -func (self FakeHeapster) ID() integrationapi.IntegrationID { - return "fakeHeapster" -} - -func (self FakeRequest) DoRaw(ctx context.Context) ([]byte, error) { - _NumRequests.increment() - log.Println("Performing req...") - path := self.Path - time.Sleep(50 * time.Millisecond) // simulate response delay of 0.05 seconds - if strings.Contains(path, "/pod-list/") { - r, _ := regexp.Compile(`\/pod\-list\/(.+)\/metrics\/`) - submatch := r.FindStringSubmatch(path) - if len(submatch) != 2 { - return nil, fmt.Errorf("Invalid request url %s", path) - } - requestedPods := strings.Split(submatch[1], ",") - - r, _ = regexp.Compile(`\/namespaces\/(.+)\/pod\-list\/`) - submatch = r.FindStringSubmatch(path) - if len(submatch) != 2 { - return nil, fmt.Errorf("Invalid request url %s", path) - } - namespace := submatch[1] - - items := []heapster.MetricResult{} - for _, pod := range requestedPods { - items = append(items, heapster.MetricResult{Metrics: self.PodData[pod+"/"+namespace]}) - } - x, err := json.Marshal(heapster.MetricResultList{Items: items}) - log.Println("Got you:", string(x)) - return x, err - - } else if strings.Contains(path, "/nodes/") { - r, _ := regexp.Compile(`\/nodes\/(.+)\/metrics\/`) - submatch := r.FindStringSubmatch(path) - if len(submatch) != 2 { - return nil, fmt.Errorf("Invalid request url %s", path) - } - requestedNode := submatch[1] - - x, err := json.Marshal(heapster.MetricResult{Metrics: self.NodeData[requestedNode]}) - log.Println("Got you:", string(x)) - return x, err - } else { - return nil, fmt.Errorf("Invalid request url %s", path) - } -} - -func (self FakeRequest) AbsPath(segments ...string) *rest.Request { - return &rest.Request{} -} - -const TimeTemplate = "2016-08-12T11:0%d:00Z" -const TimeTemplateValue = int64(1470999600) - -func NewRawDPs(dps []int64, startTime int) []heapster.MetricPoint { - newRdps := []heapster.MetricPoint{} - for i := 0; i < len(dps) && startTime+i < 10; i++ { - parsedTime, _ := time.Parse(time.RFC3339, fmt.Sprintf(TimeTemplate, i+startTime)) - newRdps = append(newRdps, heapster.MetricPoint{Timestamp: parsedTime, Value: uint64(dps[i])}) - } - return newRdps -} - -func newDps(dps []int64, startTime int) metricapi.DataPoints { - newDps := metricapi.DataPoints{} - for i := 0; i < len(dps) && startTime+i < 10; i++ { - newDps = append(newDps, metricapi.DataPoint{X: TimeTemplateValue + int64(60*(i+startTime)), Y: dps[i]}) - } - return newDps -} - -var fakePodData = PodData{ - "P1/a": NewRawDPs([]int64{0, 5, 10}, 0), - "P2/a": NewRawDPs([]int64{15, 20, 25}, 0), - "P3/a": NewRawDPs([]int64{30, 35, 40}, 0), - "P4/a": NewRawDPs([]int64{45, 50, -100000}, 0), - "P1/b": NewRawDPs([]int64{1000, 1100}, 0), - "P2/b": NewRawDPs([]int64{1200, 1300}, 1), - "P3/b": NewRawDPs([]int64{1400, 1500}, 2), - "P4/b": NewRawDPs([]int64{}, 0), - "P1/c": NewRawDPs([]int64{10000, 11000, 12000}, 0), - "P2/c": NewRawDPs([]int64{13000, 14000, 15000}, 0), -} - -var fakeNodeData = NodeData{ - "N1": NewRawDPs([]int64{0, 5, 10}, 0), - "N2": NewRawDPs([]int64{15, 20, 25}, 0), - "N3": NewRawDPs([]int64{30, 35, 40}, 0), - "N4": NewRawDPs([]int64{45, 50, 55}, 0), -} - -var fakeHeapsterClient = FakeHeapster{ - PodData: fakePodData, - NodeData: fakeNodeData, -} - -func getResourceSelector(namespace string, resourceType api.ResourceKind, - resourceName, uid string) metricapi.ResourceSelector { - return metricapi.ResourceSelector{ - Namespace: namespace, - ResourceType: resourceType, - ResourceName: resourceName, - UID: types.UID(uid), - } -} - -func TestDownloadMetric(t *testing.T) { - type HeapsterSelectorTestCase struct { - Info string - Selectors []metricapi.ResourceSelector - ExpectedDataPoints metricapi.DataPoints - ExpectedNumRequests int - } - testCases := []HeapsterSelectorTestCase{ - { - "get data for single pod", - []metricapi.ResourceSelector{ - getResourceSelector("a", api.ResourceKindPod, "P1", "U1"), - }, - newDps([]int64{0, 5, 10}, 0), - 1, - }, - { - "get data for 3 pods", - []metricapi.ResourceSelector{ - getResourceSelector("a", api.ResourceKindPod, "P1", "U1"), - getResourceSelector("a", api.ResourceKindPod, "P2", "U2"), - getResourceSelector("a", api.ResourceKindPod, "P3", "U3"), - }, - newDps([]int64{45, 60, 75}, 0), - 1, - }, - { - "get data for 4 pods where 1 pod does not exist - ignore non existing pod", - []metricapi.ResourceSelector{ - getResourceSelector("a", api.ResourceKindPod, "P1", "U1"), - getResourceSelector("a", api.ResourceKindPod, "P2", "U2"), - getResourceSelector("a", api.ResourceKindPod, "P3", "U3"), - getResourceSelector("a", api.ResourceKindPod, "NON_EXISTING", "NA"), - }, - newDps([]int64{45, 60, 75}, 0), - 1, - }, - { - "get data for 4 pods where pods have different X timestams available", - []metricapi.ResourceSelector{ - getResourceSelector("b", api.ResourceKindPod, "P1", "U1"), - getResourceSelector("b", api.ResourceKindPod, "P2", "U2"), - getResourceSelector("b", api.ResourceKindPod, "P3", "U3"), - getResourceSelector("b", api.ResourceKindPod, "P4", "U4"), - }, - newDps([]int64{1000, 2300, 2700, 1500}, 0), - 1, - }, - { - "ask for non existing namespace - return no data points", - []metricapi.ResourceSelector{ - getResourceSelector("NON_EXISTING_NAMESPACE", api.ResourceKindPod, - "P1", "U1"), - }, - newDps([]int64{}, 0), - 1, - }, - { - "get data for 0 pods - return no data points", - []metricapi.ResourceSelector{}, - newDps([]int64{}, 0), - 0, - }, - { - "get data for 0 nodes - return no data points", - []metricapi.ResourceSelector{}, - newDps([]int64{}, 0), - 0, - }, - { - "ask for 1 node", - []metricapi.ResourceSelector{ - getResourceSelector("NO_NAMESPACE", api.ResourceKindNode, "N1", - "U11"), - }, - newDps([]int64{0, 5, 10}, 0), - 1, - }, - { - "ask for 3 nodes", - []metricapi.ResourceSelector{ - getResourceSelector("NO_NAMESPACE", api.ResourceKindNode, "N1", - "U11"), - getResourceSelector("NO_NAMESPACE", api.ResourceKindNode, "N2", - "U12"), - getResourceSelector("NO_NAMESPACE", api.ResourceKindNode, "N3", - "U13"), - }, - newDps([]int64{45, 60, 75}, 0), - 3, // change this to 1 when nodes support all in 1 download. - }, - } - for _, testCase := range testCases { - log.Println("-----------\n\n\n", testCase.Info, int(_NumRequests.get())) - hClient := heapsterClient{fakeHeapsterClient} - promises := hClient.DownloadMetric(testCase.Selectors, "", - &metricapi.CachedResources{}) - metrics, err := hClient.AggregateMetrics(promises, "", nil).GetMetrics() - if err != nil { - t.Errorf("Test Case: %s. Failed to get metrics - %s", testCase.Info, err) - return - } - numReq := fakeHeapsterClient.GetNumberOfRequestsMade() - - if !reflect.DeepEqual(metrics[0].DataPoints, testCase.ExpectedDataPoints) { - t.Errorf("Test Case: %s. Received incorrect data points. Got %v, expected %v.", - testCase.Info, metrics[0].DataPoints, testCase.ExpectedDataPoints) - } - - if testCase.ExpectedNumRequests != numReq { - t.Errorf("Test Case: %s. Selector performed unexpected number of requests to the heapster server. Performed %d, expected %d", - testCase.Info, numReq, testCase.ExpectedNumRequests) - } - } -} - -var selectorPool = []metricapi.ResourceSelector{ - getResourceSelector("a", api.ResourceKindPod, "P1", "U1"), - getResourceSelector("a", api.ResourceKindPod, "P2", "U2"), - getResourceSelector("a", api.ResourceKindPod, "P3", "U3"), - getResourceSelector("b", api.ResourceKindPod, "P1", "Z1"), - getResourceSelector("b", api.ResourceKindPod, "P2", "Z2"), - getResourceSelector("b", api.ResourceKindPod, "P3", "Z3"), - getResourceSelector("NO_NAMESPACE", api.ResourceKindNode, "N1", "U11"), - getResourceSelector("NO_NAMESPACE", api.ResourceKindNode, "N2", "U12"), - getResourceSelector("NO_NAMESPACE", api.ResourceKindNode, "N3", "U13"), - getResourceSelector("NO_NAMESPACE", api.ResourceKindNode, "N4", "U14"), -} - -func TestDownloadMetrics(t *testing.T) { - type HeapsterSelectorsTestCase struct { - Info string - SelectorIds []int - AggregationNames metricapi.AggregationModes - MetricNames []string - ExpectedDataPoints []metricapi.DataPoints - ExpectedNumRequests int - } - - MinMaxSumAggregations := metricapi.AggregationModes{metricapi.MinAggregation, - metricapi.MaxAggregation, metricapi.SumAggregation} - testCases := []HeapsterSelectorsTestCase{ - { - "ask for 1 resource", - []int{1}, - MinMaxSumAggregations, - []string{"Dummy/Metric"}, - []metricapi.DataPoints{ - newDps([]int64{15, 20, 25}, 0), - newDps([]int64{15, 20, 25}, 0), - newDps([]int64{15, 20, 25}, 0), - }, - 1, - }, - { - "ask for 2 resources from same namespace", - []int{0, 1}, - MinMaxSumAggregations, - []string{"Dummy/Metric"}, - []metricapi.DataPoints{ - newDps([]int64{0, 5, 10}, 0), - newDps([]int64{15, 20, 25}, 0), - newDps([]int64{15, 25, 35}, 0), - }, - 1, - }, - { - "ask for 3 resources from same namespace, get 2 metrics", - []int{0, 1, 2}, - MinMaxSumAggregations, - []string{"Dummy/Metric1", "DummyMetric2"}, - []metricapi.DataPoints{ - newDps([]int64{0, 5, 10}, 0), - newDps([]int64{30, 35, 40}, 0), - newDps([]int64{45, 60, 75}, 0), - newDps([]int64{0, 5, 10}, 0), - newDps([]int64{30, 35, 40}, 0), - newDps([]int64{45, 60, 75}, 0), - }, - 2, - }, - { - "ask for multiple resources of the same kind from multiple namespaces", - []int{0, 1, 3, 4}, - MinMaxSumAggregations, - []string{"Dummy/Metric"}, - []metricapi.DataPoints{ - newDps([]int64{0, 5, 10}, 0), - newDps([]int64{1000, 1200, 1300}, 0), - newDps([]int64{1015, 2325, 1335}, 0), - }, - 2, - }, - { - "ask for multiple resources of different kind from multiple namespaces", - []int{0, 1, 6, 7}, - MinMaxSumAggregations, - []string{"Dummy/Metric"}, - []metricapi.DataPoints{ - newDps([]int64{0, 5, 10}, 0), - newDps([]int64{15, 20, 25}, 0), - newDps([]int64{30, 50, 70}, 0), - }, - 3, // if we had node-list option in heapster API we would make only 2 - // requests unfortunately there is no such option and we have to make one request per node - // note that nodes overlap (1,2,3) + (3,4) and we download node 3 only once thanks to request compression - // So 4 requests for nodes (one for each unique node) and 2 requests for pods (1 for each namespace) = 6 in total. - }, - } - - for _, testCase := range testCases { - selectors := []metricapi.ResourceSelector{} - hClient := heapsterClient{fakeHeapsterClient} - for _, selectorId := range testCase.SelectorIds { - selectors = append(selectors, selectorPool[selectorId]) - } - - metricPromises := make(metricapi.MetricPromises, 0) - for _, metricName := range testCase.MetricNames { - promises := hClient.DownloadMetric(selectors, metricName, - &metricapi.CachedResources{}) - promises = hClient.AggregateMetrics(promises, metricName, - testCase.AggregationNames) - metricPromises = append(metricPromises, promises...) - } - metrics, err := metricPromises.GetMetrics() - if err != nil { - t.Errorf("Test Case: %s. Failed to get metrics - %s", testCase.Info, err) - return - } - - receivedDataPoints := []metricapi.DataPoints{} - for _, metric := range metrics { - receivedDataPoints = append(receivedDataPoints, metric.DataPoints) - } - - if !reflect.DeepEqual(receivedDataPoints, testCase.ExpectedDataPoints) { - t.Errorf("Test Case: %s. Received incorrect data points. Got %v, expected %v.", - testCase.Info, receivedDataPoints, testCase.ExpectedDataPoints) - } - numReq := fakeHeapsterClient.GetNumberOfRequestsMade() - if testCase.ExpectedNumRequests != numReq { - t.Errorf("Test Case: %s. Selector performed unexpected number of requests to the heapster server. Performed %d, expected %d", - testCase.Info, numReq, testCase.ExpectedNumRequests) - } - } -} - -func TestCreateHeapsterClient(t *testing.T) { - k8sClient := client.NewClientManager("", "http://localhost:8080").InsecureClient() - cases := []struct { - info string - heapsterHost string - client kubernetes.Interface - expected HeapsterRESTClient - expectedErr error - }{ - { - "should create in-cluster heapster client", - "", - k8sClient, - inClusterHeapsterClient{}, - nil, - }, - { - "should create remote heapster client", - "http://localhost:80801", - nil, - remoteHeapsterClient{}, - nil, - }, - { - "should return error", - "invalid-url-!!23*%.", - nil, - nil, - errors.NewInvalid("parse http://invalid-url-!!23*%.: invalid URL escape \"%.\""), - }, - } - - for _, c := range cases { - metricClient, err := CreateHeapsterClient(c.heapsterHost, c.client) - - if !areErrorsEqual(c.expectedErr, err) { - t.Errorf("Test Case: %s. Expected error to be: %v, but got %v.", - c.info, c.expectedErr, err) - } - - heapsterClient, _ := metricClient.(heapsterClient) - if reflect.TypeOf(heapsterClient.client) != reflect.TypeOf(c.expected) { - t.Errorf("Test Case: %s. Expected client to be of type: %v, but got %v", - c.info, reflect.TypeOf(c.expected), reflect.TypeOf(heapsterClient.client)) - } - } -} diff --git a/src/app/backend/integration/metric/heapster/common.go b/src/app/backend/integration/metric/heapster/common.go deleted file mode 100644 index e8e2867d65e8..000000000000 --- a/src/app/backend/integration/metric/heapster/common.go +++ /dev/null @@ -1,89 +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 heapster - -import ( - "github.com/kubernetes/dashboard/src/app/backend/api" - metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" - heapster "k8s.io/heapster/metrics/api/v1/types" -) - -// compress compresses list of HeapsterSelectors to equivalent, shorter one in order to perform smaller number of requests. -// For example if we have 2 HeapsterSelectors, first downloading data for pods A, B and second one downloading data for pods B,C. -// compress will compress this to just one HeapsterSelector downloading data for A,B,C. Reverse mapping returned provides -// a mapping between indices from new compressed list to the list of children indices from original list. -func compress(selectors []heapsterSelector) ([]heapsterSelector, map[string][]int) { - reverseMapping := map[string][]int{} - resourceTypeMap := map[string]api.ResourceKind{} - resourceMap := map[string][]string{} - labelMap := map[string]metricapi.Label{} - for i, selector := range selectors { - entry := selector.Path - resources, doesEntryExist := resourceMap[selector.Path] - // compress resources - resourceMap[entry] = append(resources, selector.Resources...) - - // compress labels - if !doesEntryExist { - resourceTypeMap[entry] = selector.TargetResourceType // this will be the same for all entries - labelMap[entry] = metricapi.Label{} - } - labelMap[entry].AddMetricLabel(selector.Label) - reverseMapping[entry] = append(reverseMapping[entry], i) - } - - for entry, resources := range resourceMap { - resourceMap[entry] = toUniqueSlice(resources) - } - - // create new compressed HeapsterSelectors. - compressed := make([]heapsterSelector, 0) - for entry, resourceType := range resourceTypeMap { - newSelector := heapsterSelector{ - Path: entry, - Resources: resourceMap[entry], - Label: labelMap[entry], - TargetResourceType: resourceType, - } - compressed = append(compressed, newSelector) - } - return compressed, reverseMapping -} - -func toUniqueSlice(strings []string) []string { - result := make([]string, 0) - uniquenessMap := make(map[string]bool) - for _, s := range strings { - if _, exists := uniquenessMap[s]; !exists { - result = append(result, s) - } - - uniquenessMap[s] = true - } - - return result -} - -func toMetricPoints(heapsterMetricPoint []heapster.MetricPoint) []metricapi.MetricPoint { - metricPoints := make([]metricapi.MetricPoint, len(heapsterMetricPoint)) - for i, heapsterMP := range heapsterMetricPoint { - metricPoints[i] = metricapi.MetricPoint{ - Value: heapsterMP.Value, - Timestamp: heapsterMP.Timestamp, - } - } - - return metricPoints -} diff --git a/src/app/backend/integration/metric/heapster/model.go b/src/app/backend/integration/metric/heapster/model.go deleted file mode 100644 index 68d73b7a191f..000000000000 --- a/src/app/backend/integration/metric/heapster/model.go +++ /dev/null @@ -1,46 +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 heapster - -import ( - "github.com/kubernetes/dashboard/src/app/backend/api" - metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" - heapster "k8s.io/heapster/metrics/api/v1/types" -) - -// HeapsterAllInOneDownloadConfig holds config information specifying whether given native Heapster -// resource type supports list download. -var HeapsterAllInOneDownloadConfig = map[api.ResourceKind]bool{ - api.ResourceKindPod: true, - api.ResourceKindNode: false, -} - -// DataPointsFromMetricJSONFormat converts all the data points from format used by heapster to our -// format. -func DataPointsFromMetricJSONFormat(raw heapster.MetricResult) (dp metricapi.DataPoints) { - for _, raw := range raw.Metrics { - converted := metricapi.DataPoint{ - X: raw.Timestamp.Unix(), - Y: int64(raw.Value), - } - - if converted.Y < 0 { - converted.Y = 0 - } - - dp = append(dp, converted) - } - return -} diff --git a/src/app/backend/integration/metric/heapster/restclient.go b/src/app/backend/integration/metric/heapster/restclient.go deleted file mode 100644 index 7a142b646987..000000000000 --- a/src/app/backend/integration/metric/heapster/restclient.go +++ /dev/null @@ -1,84 +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 heapster - -import ( - "context" - - "k8s.io/client-go/rest" -) - -// HeapsterRESTClient is used to make raw requests to heapster. -type HeapsterRESTClient interface { - // Creates a new GET HTTP request to heapster, specified by the path param, to the V1 API - // endpoint. The path param is without the API prefix, e.g., - // /model/namespaces/default/pod-list/foo/metrics/memory-usage - Get(path string) RequestInterface - HealthCheck() error -} - -// RequestInterface is an interface that allows to make operations on pure request object. -// Separation is done to allow testing. -type RequestInterface interface { - DoRaw(context.Context) ([]byte, error) - AbsPath(segments ...string) *rest.Request -} - -// InClusterHeapsterClient is an in-cluster implementation of a Heapster client. Talks with Heapster -// through service proxy. -type inClusterHeapsterClient struct { - client rest.Interface -} - -// Get creates request to given path. -func (c inClusterHeapsterClient) Get(path string) RequestInterface { - return c.client.Get(). - Namespace("kube-system"). - Resource("services"). - Name("heapster"). - SubResource("proxy"). - Suffix("/api/v1/" + path) -} - -// HealthCheck does a health check of the application. -// Returns nil if connection to application can be established, error object otherwise. -func (self inClusterHeapsterClient) HealthCheck() error { - _, err := self.client.Get(). - Namespace("kube-system"). - Resource("services"). - Name("heapster"). - SubResource("proxy"). - Suffix("/healthz"). - DoRaw(context.TODO()) - return err -} - -// RemoteHeapsterClient is an implementation of a remote Heapster client. Talks with Heapster -// through raw RESTClient. -type remoteHeapsterClient struct { - client rest.Interface -} - -// Get creates request to given path. -func (c remoteHeapsterClient) Get(path string) RequestInterface { - return c.client.Get().Suffix(path) -} - -// HealthCheck does a health check of the application. -// Returns nil if connection to application can be established, error object otherwise. -func (self remoteHeapsterClient) HealthCheck() error { - _, err := self.Get("healthz").AbsPath("/").DoRaw(context.TODO()) - return err -} diff --git a/src/app/backend/integration/metric/heapster/selector.go b/src/app/backend/integration/metric/heapster/selector.go deleted file mode 100644 index 74648e4a29da..000000000000 --- a/src/app/backend/integration/metric/heapster/selector.go +++ /dev/null @@ -1,137 +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 heapster - -import ( - "fmt" - - "github.com/emicklei/go-restful/v3/log" - "github.com/kubernetes/dashboard/src/app/backend/api" - metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" -) - -type heapsterSelector struct { - TargetResourceType api.ResourceKind - Path string - Resources []string - metricapi.Label -} - -func getHeapsterSelectors(selectors []metricapi.ResourceSelector, - cachedResources *metricapi.CachedResources) []heapsterSelector { - result := make([]heapsterSelector, len(selectors)) - for i, selector := range selectors { - heapsterSelector, err := getHeapsterSelector(selector, cachedResources) - if err != nil { - log.Printf("There was an error during transformation to heapster selector: %s", err.Error()) - continue - } - - result[i] = heapsterSelector - } - - return result -} - -func getHeapsterSelector(selector metricapi.ResourceSelector, - cachedResources *metricapi.CachedResources) (heapsterSelector, error) { - summingResource, isDerivedResource := metricapi.DerivedResources[selector.ResourceType] - if !isDerivedResource { - return newHeapsterSelectorFromNativeResource(selector.ResourceType, selector.Namespace, - []string{selector.ResourceName}, []types.UID{selector.UID}) - } - // We are dealing with derived resource. Convert derived resource to its native resources. - // For example, convert deployment to the list of pod names that belong to this deployment - if summingResource == api.ResourceKindPod { - myPods, err := getMyPodsFromCache(selector, cachedResources.Pods) - if err != nil { - return heapsterSelector{}, err - } - return newHeapsterSelectorFromNativeResource(api.ResourceKindPod, - selector.Namespace, podListToNameList(myPods), podListToUIDList(myPods)) - } - // currently can only convert derived resource to pods. You can change it by implementing other methods - return heapsterSelector{}, fmt.Errorf(`Internal Error: Requested summing resources not supported. Requested "%s"`, summingResource) -} - -// getMyPodsFromCache returns a full list of pods that belong to this resource. -// It is important that cachedPods include ALL pods from the namespace of this resource (but they -// can also include pods from other namespaces). -func getMyPodsFromCache(selector metricapi.ResourceSelector, cachedPods []v1.Pod) (matchingPods []v1.Pod, err error) { - switch { - case cachedPods == nil: - err = fmt.Errorf(`Pods were not available in cache. Required for resource type: "%s"`, - selector.ResourceType) - case selector.ResourceType == api.ResourceKindDeployment: - for _, pod := range cachedPods { - if pod.ObjectMeta.Namespace == selector.Namespace && api.IsSelectorMatching(selector.Selector, pod.Labels) { - matchingPods = append(matchingPods, pod) - } - } - default: - for _, pod := range cachedPods { - if pod.Namespace == selector.Namespace { - for _, ownerRef := range pod.OwnerReferences { - if ownerRef.Controller != nil && *ownerRef.Controller == true && - ownerRef.UID == selector.UID { - matchingPods = append(matchingPods, pod) - } - } - } - } - } - return -} - -// NewHeapsterSelectorFromNativeResource returns new heapster selector for native resources specified in arguments. -// returns error if requested resource is not native or is not supported. -func newHeapsterSelectorFromNativeResource(resourceType api.ResourceKind, namespace string, - resourceNames []string, resourceUIDs []types.UID) (heapsterSelector, error) { - // Here we have 2 possibilities because this module allows downloading Nodes and Pods from heapster - if resourceType == api.ResourceKindPod { - return heapsterSelector{ - TargetResourceType: api.ResourceKindPod, - Path: `namespaces/` + namespace + `/pod-list/`, - Resources: resourceNames, - Label: metricapi.Label{resourceType: resourceUIDs}, - }, nil - } else if resourceType == api.ResourceKindNode { - return heapsterSelector{ - TargetResourceType: api.ResourceKindNode, - Path: `nodes/`, - Resources: resourceNames, - Label: metricapi.Label{resourceType: resourceUIDs}, - }, nil - } else { - return heapsterSelector{}, fmt.Errorf(`Resource "%s" is not a native heapster resource type or is not supported`, resourceType) - } -} - -// podListToNameList converts list of pods to the list of pod names. -func podListToNameList(podList []v1.Pod) (result []string) { - for _, pod := range podList { - result = append(result, pod.Name) - } - return -} - -func podListToUIDList(podList []v1.Pod) (result []types.UID) { - for _, pod := range podList { - result = append(result, pod.UID) - } - return -} diff --git a/src/app/backend/integration/metric/heapster/selector_test.go b/src/app/backend/integration/metric/heapster/selector_test.go deleted file mode 100644 index 98f2a731b46b..000000000000 --- a/src/app/backend/integration/metric/heapster/selector_test.go +++ /dev/null @@ -1,133 +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 heapster - -import ( - "reflect" - "testing" - - "github.com/kubernetes/dashboard/src/app/backend/api" - metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" - v1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestGetHeapsterSelector(t *testing.T) { - resource1 := map[string]string{ - "resource": "1", - } - resource2 := map[string]string{ - "resource": "2", - } - var cachedPodList = []v1.Pod{ - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "1", - Labels: resource1, - Namespace: "a", - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "2", - Labels: resource2, - Namespace: "a", - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "3", - Labels: resource1, - Namespace: "a", - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "4", - Labels: resource1, - Namespace: "b", - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "5", - Labels: resource1, - }, - }, - } - testCases := []struct { - Info string - ResourceSelector metricapi.ResourceSelector - ExpectedPath string - ExpectedTargetResource api.ResourceKind - ExpectedResources []string - }{ - { - "ResourceSelector for native resource - pod", - metricapi.ResourceSelector{ - Namespace: "bar", - ResourceType: api.ResourceKindPod, - ResourceName: "foo", - }, - `namespaces/bar/pod-list/`, - api.ResourceKindPod, - []string{"foo"}, - }, - { - "ResourceSelector for native resource - node", - metricapi.ResourceSelector{ - Namespace: "barn", - ResourceType: api.ResourceKindNode, - ResourceName: "foon", - }, - `nodes/`, - api.ResourceKindNode, - []string{"foon"}, - }, - { - "ResourceSelector for derived resource with old style selector", - metricapi.ResourceSelector{ - Namespace: "a", - ResourceType: api.ResourceKindDeployment, - ResourceName: "baba", - Selector: resource1, - }, - `namespaces/a/pod-list/`, - api.ResourceKindPod, - []string{"1", "3"}, - }, - } - for _, testCase := range testCases { - sel, err := getHeapsterSelector(testCase.ResourceSelector, - &metricapi.CachedResources{Pods: cachedPodList}) - if err != nil { - t.Errorf("Test Case: %s. Failed to get HeapsterSelector. - %s", testCase.Info, err) - return - } - if !reflect.DeepEqual(sel.Resources, testCase.ExpectedResources) { - t.Errorf("Test Case: %s. Converted resource selector to incorrect native resources. Got %v, expected %v.", - testCase.Info, sel.Resources, testCase.ExpectedResources) - } - if sel.TargetResourceType != testCase.ExpectedTargetResource { - t.Errorf("Test Case: %s. Used invalid target resource type. Got %s, expected %s.", - testCase.Info, sel.TargetResourceType, testCase.ExpectedTargetResource) - } - if sel.Path != testCase.ExpectedPath { - t.Errorf("Test Case: %s. Converted to invalid heapster download path. Got %s, expected %s.", - testCase.Info, sel.Path, testCase.ExpectedPath) - } - - } -} diff --git a/src/app/backend/integration/metric/manager.go b/src/app/backend/integration/metric/manager.go index 2824d71ca6c9..591652f09f92 100644 --- a/src/app/backend/integration/metric/manager.go +++ b/src/app/backend/integration/metric/manager.go @@ -19,12 +19,12 @@ import ( "log" "time" + "k8s.io/apimachinery/pkg/util/wait" + clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api" integrationapi "github.com/kubernetes/dashboard/src/app/backend/integration/api" metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" - "github.com/kubernetes/dashboard/src/app/backend/integration/metric/heapster" "github.com/kubernetes/dashboard/src/app/backend/integration/metric/sidecar" - "k8s.io/apimachinery/pkg/util/wait" ) // MetricManager is responsible for management of all integrated applications related to metrics. @@ -43,8 +43,6 @@ type MetricManager interface { List() []integrationapi.Integration // ConfigureSidecar configures and adds sidecar to clients list. ConfigureSidecar(host string) MetricManager - // ConfigureHeapster configures and adds sidecar to clients list. - ConfigureHeapster(host string) MetricManager } // Implements MetricManager interface. @@ -130,19 +128,6 @@ func (self *metricManager) ConfigureSidecar(host string) MetricManager { return self } -// ConfigureHeapster implements metric manager interface. See MetricManager for more information. -func (self *metricManager) ConfigureHeapster(host string) MetricManager { - kubeClient := self.manager.InsecureClient() - metricClient, err := heapster.CreateHeapsterClient(host, kubeClient) - if err != nil { - log.Printf("There was an error during heapster client creation: %s", err.Error()) - return self - } - - self.clients[metricClient.ID()] = metricClient - return self -} - // NewMetricManager creates metric manager. func NewMetricManager(manager clientapi.ClientManager) MetricManager { return &metricManager{ diff --git a/src/app/backend/integration/metric/manager_test.go b/src/app/backend/integration/metric/manager_test.go index 678d8523b0d9..292ad2306073 100644 --- a/src/app/backend/integration/metric/manager_test.go +++ b/src/app/backend/integration/metric/manager_test.go @@ -18,7 +18,6 @@ import ( "reflect" "testing" - "github.com/kubernetes/dashboard/src/app/backend/client" "github.com/kubernetes/dashboard/src/app/backend/errors" integrationapi "github.com/kubernetes/dashboard/src/app/backend/integration/api" "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" @@ -132,21 +131,3 @@ func TestMetricManager_List(t *testing.T) { } } } - -func TestMetricManager_ConfigureHeapster(t *testing.T) { - cases := []struct { - manager MetricManager - expectedClients int - }{ - {NewMetricManager(client.NewClientManager("", "http://localhost:8080")), 1}, - } - - for _, c := range cases { - c.manager.ConfigureHeapster("") - - if len(c.manager.List()) != c.expectedClients { - t.Errorf("Failed to configure heapster. Expected number of clients to be "+ - "%d, but got %d.", c.expectedClients, len(c.manager.List())) - } - } -} diff --git a/src/app/backend/resource/common/resourcechannels.go b/src/app/backend/resource/common/resourcechannels.go index 32507b30331e..8d72dbfba8cb 100644 --- a/src/app/backend/resource/common/resourcechannels.go +++ b/src/app/backend/resource/common/resourcechannels.go @@ -20,7 +20,6 @@ import ( apps "k8s.io/api/apps/v1" autoscaling "k8s.io/api/autoscaling/v1" batch "k8s.io/api/batch/v1" - batch2 "k8s.io/api/batch/v1beta1" v1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" rbac "k8s.io/api/rbac/v1" @@ -559,20 +558,20 @@ func GetJobListChannel(client client.Interface, // CronJobListChannel is a list and error channels to Cron Jobs. type CronJobListChannel struct { - List chan *batch2.CronJobList + List chan *batch.CronJobList Error chan error } // GetCronJobListChannel returns a pair of channels to a Cron Job list and errors that both must be read numReads times. func GetCronJobListChannel(client client.Interface, nsQuery *NamespaceQuery, numReads int) CronJobListChannel { channel := CronJobListChannel{ - List: make(chan *batch2.CronJobList, numReads), + List: make(chan *batch.CronJobList, numReads), Error: make(chan error, numReads), } go func() { - list, err := client.BatchV1beta1().CronJobs(nsQuery.ToRequestParam()).List(context.TODO(), api.ListEverything) - var filteredItems []batch2.CronJob + list, err := client.BatchV1().CronJobs(nsQuery.ToRequestParam()).List(context.TODO(), api.ListEverything) + var filteredItems []batch.CronJob for _, item := range list.Items { if nsQuery.Matches(item.ObjectMeta.Namespace) { filteredItems = append(filteredItems, item) diff --git a/src/app/backend/resource/cronjob/common.go b/src/app/backend/resource/cronjob/common.go index becbfb5efa0e..d0f55ef38133 100644 --- a/src/app/backend/resource/cronjob/common.go +++ b/src/app/backend/resource/cronjob/common.go @@ -15,16 +15,17 @@ package cronjob import ( + batch "k8s.io/api/batch/v1" + "github.com/kubernetes/dashboard/src/app/backend/api" metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" - batchv1beta1 "k8s.io/api/batch/v1beta1" ) // The code below allows to perform complex data section on []batch.CronJob -type CronJobCell batchv1beta1.CronJob +type CronJobCell batch.CronJob func (self CronJobCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue { switch name { @@ -49,7 +50,7 @@ func (self CronJobCell) GetResourceSelector() *metricapi.ResourceSelector { } } -func ToCells(std []batchv1beta1.CronJob) []dataselect.DataCell { +func ToCells(std []batch.CronJob) []dataselect.DataCell { cells := make([]dataselect.DataCell, len(std)) for i := range std { cells[i] = CronJobCell(std[i]) @@ -57,15 +58,15 @@ func ToCells(std []batchv1beta1.CronJob) []dataselect.DataCell { return cells } -func FromCells(cells []dataselect.DataCell) []batchv1beta1.CronJob { - std := make([]batchv1beta1.CronJob, len(cells)) +func FromCells(cells []dataselect.DataCell) []batch.CronJob { + std := make([]batch.CronJob, len(cells)) for i := range std { - std[i] = batchv1beta1.CronJob(cells[i].(CronJobCell)) + std[i] = batch.CronJob(cells[i].(CronJobCell)) } return std } -func getStatus(list *batchv1beta1.CronJobList) common.ResourceStatus { +func getStatus(list *batch.CronJobList) common.ResourceStatus { info := common.ResourceStatus{} if list == nil { return info @@ -82,7 +83,7 @@ func getStatus(list *batchv1beta1.CronJobList) common.ResourceStatus { return info } -func getContainerImages(cronJob *batchv1beta1.CronJob) []string { +func getContainerImages(cronJob *batch.CronJob) []string { podSpec := cronJob.Spec.JobTemplate.Spec.Template.Spec result := make([]string, 0) diff --git a/src/app/backend/resource/cronjob/detail.go b/src/app/backend/resource/cronjob/detail.go index 8becbe6c8450..c41bcdfac323 100644 --- a/src/app/backend/resource/cronjob/detail.go +++ b/src/app/backend/resource/cronjob/detail.go @@ -17,7 +17,7 @@ package cronjob import ( "context" - batch2 "k8s.io/api/batch/v1beta1" + batch "k8s.io/api/batch/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sClient "k8s.io/client-go/kubernetes" ) @@ -37,7 +37,7 @@ type CronJobDetail struct { // GetCronJobDetail gets Cron Job details. func GetCronJobDetail(client k8sClient.Interface, namespace, name string) (*CronJobDetail, error) { - rawObject, err := client.BatchV1beta1().CronJobs(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) + rawObject, err := client.BatchV1().CronJobs(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) if err != nil { return nil, err } @@ -46,7 +46,7 @@ func GetCronJobDetail(client k8sClient.Interface, namespace, name string) (*Cron return &cj, nil } -func toCronJobDetail(cj *batch2.CronJob) CronJobDetail { +func toCronJobDetail(cj *batch.CronJob) CronJobDetail { return CronJobDetail{ CronJob: toCronJob(cj), ConcurrencyPolicy: string(cj.Spec.ConcurrencyPolicy), diff --git a/src/app/backend/resource/cronjob/detail_test.go b/src/app/backend/resource/cronjob/detail_test.go index 09dbd69ccc52..5918cc7c1bde 100644 --- a/src/app/backend/resource/cronjob/detail_test.go +++ b/src/app/backend/resource/cronjob/detail_test.go @@ -18,12 +18,13 @@ import ( "reflect" "testing" + batch "k8s.io/api/batch/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + "github.com/kubernetes/dashboard/src/app/backend/api" "github.com/kubernetes/dashboard/src/app/backend/resource/cronjob" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" - batch "k8s.io/api/batch/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" ) func TestGetCronJobDetail(t *testing.T) { diff --git a/src/app/backend/resource/cronjob/events.go b/src/app/backend/resource/cronjob/events.go index bd13d936fe38..f062c33c77f2 100644 --- a/src/app/backend/resource/cronjob/events.go +++ b/src/app/backend/resource/cronjob/events.go @@ -15,10 +15,11 @@ package cronjob import ( + client "k8s.io/client-go/kubernetes" + "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" "github.com/kubernetes/dashboard/src/app/backend/resource/event" - client "k8s.io/client-go/kubernetes" ) // GetCronJobEvents gets events associated to cron job. diff --git a/src/app/backend/resource/cronjob/events_test.go b/src/app/backend/resource/cronjob/events_test.go index 0bf93b24ce07..394a30fa3a86 100644 --- a/src/app/backend/resource/cronjob/events_test.go +++ b/src/app/backend/resource/cronjob/events_test.go @@ -18,13 +18,14 @@ import ( "reflect" "testing" + v1 "k8s.io/api/core/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + "github.com/kubernetes/dashboard/src/app/backend/api" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/cronjob" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" - v1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" ) func TestGetJobEvents(t *testing.T) { diff --git a/src/app/backend/resource/cronjob/jobs.go b/src/app/backend/resource/cronjob/jobs.go index 7c4aef0424de..9731c66eddf5 100644 --- a/src/app/backend/resource/cronjob/jobs.go +++ b/src/app/backend/resource/cronjob/jobs.go @@ -17,17 +17,23 @@ package cronjob import ( "context" + batch "k8s.io/api/batch/v1" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/rand" + client "k8s.io/client-go/kubernetes" + "github.com/kubernetes/dashboard/src/app/backend/api" "github.com/kubernetes/dashboard/src/app/backend/errors" metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" "github.com/kubernetes/dashboard/src/app/backend/resource/job" - batch "k8s.io/api/batch/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/rand" - client "k8s.io/client-go/kubernetes" +) + +const ( + CronJobAPIVersion = "v1" + CronJobKindName = "cronjob" ) var emptyJobList = &job.JobList{ @@ -42,7 +48,7 @@ var emptyJobList = &job.JobList{ func GetCronJobJobs(client client.Interface, metricClient metricapi.MetricClient, dsQuery *dataselect.DataSelectQuery, namespace, name string, active bool) (*job.JobList, error) { - cronJob, err := client.BatchV1beta1().CronJobs(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) + cronJob, err := client.BatchV1beta1().CronJobs(namespace).Get(context.TODO(), name, meta.GetOptions{}) if err != nil { return emptyJobList, err } @@ -84,7 +90,7 @@ func GetCronJobJobs(client client.Interface, metricClient metricapi.MetricClient func TriggerCronJob(client client.Interface, namespace, name string) error { - cronJob, err := client.BatchV1beta1().CronJobs(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) + cronJob, err := client.BatchV1().CronJobs(namespace).Get(context.TODO(), name, meta.GetOptions{}) if err != nil { return err @@ -107,16 +113,22 @@ func TriggerCronJob(client client.Interface, } jobToCreate := &batch.Job{ - ObjectMeta: metaV1.ObjectMeta{ + ObjectMeta: meta.ObjectMeta{ Name: newJobName, Namespace: namespace, Annotations: annotations, Labels: labels, + OwnerReferences: []meta.OwnerReference{{ + APIVersion: CronJobAPIVersion, + Kind: CronJobKindName, + Name: cronJob.Name, + UID: cronJob.UID, + }}, }, Spec: cronJob.Spec.JobTemplate.Spec, } - _, err = client.BatchV1().Jobs(namespace).Create(context.TODO(), jobToCreate, metaV1.CreateOptions{}) + _, err = client.BatchV1().Jobs(namespace).Create(context.TODO(), jobToCreate, meta.CreateOptions{}) if err != nil { return err diff --git a/src/app/backend/resource/cronjob/jobs_test.go b/src/app/backend/resource/cronjob/jobs_test.go index 3ecc508648d4..09e702207baf 100644 --- a/src/app/backend/resource/cronjob/jobs_test.go +++ b/src/app/backend/resource/cronjob/jobs_test.go @@ -19,11 +19,12 @@ import ( "strings" "testing" - "github.com/kubernetes/dashboard/src/app/backend/resource/cronjob" - batch "k8s.io/api/batch/v1beta1" + batch "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/api/errors" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" + + "github.com/kubernetes/dashboard/src/app/backend/resource/cronjob" ) func TestTriggerCronJobWithInvalidName(t *testing.T) { diff --git a/src/app/backend/resource/cronjob/list.go b/src/app/backend/resource/cronjob/list.go index b7f88bee1f50..525bf5fa4909 100644 --- a/src/app/backend/resource/cronjob/list.go +++ b/src/app/backend/resource/cronjob/list.go @@ -17,14 +17,15 @@ package cronjob import ( "log" + batch "k8s.io/api/batch/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + client "k8s.io/client-go/kubernetes" + "github.com/kubernetes/dashboard/src/app/backend/api" "github.com/kubernetes/dashboard/src/app/backend/errors" metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" - "k8s.io/api/batch/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - client "k8s.io/client-go/kubernetes" ) // CronJobList contains a list of CronJobs in the cluster. @@ -82,7 +83,7 @@ func GetCronJobListFromChannels(channels *common.ResourceChannels, dsQuery *data return cronJobList, nil } -func toCronJobList(cronJobs []v1beta1.CronJob, nonCriticalErrors []error, dsQuery *dataselect.DataSelectQuery, +func toCronJobList(cronJobs []batch.CronJob, nonCriticalErrors []error, dsQuery *dataselect.DataSelectQuery, metricClient metricapi.MetricClient) *CronJobList { list := &CronJobList{ @@ -112,7 +113,7 @@ func toCronJobList(cronJobs []v1beta1.CronJob, nonCriticalErrors []error, dsQuer return list } -func toCronJob(cj *v1beta1.CronJob) CronJob { +func toCronJob(cj *batch.CronJob) CronJob { return CronJob{ ObjectMeta: api.NewObjectMeta(cj.ObjectMeta), TypeMeta: api.NewTypeMeta(api.ResourceKindCronJob), diff --git a/src/app/backend/resource/cronjob/list_test.go b/src/app/backend/resource/cronjob/list_test.go index 0e0238708fe1..5935839a4f24 100644 --- a/src/app/backend/resource/cronjob/list_test.go +++ b/src/app/backend/resource/cronjob/list_test.go @@ -18,13 +18,14 @@ import ( "reflect" "testing" + batch "k8s.io/api/batch/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kubernetes/dashboard/src/app/backend/api" metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/cronjob" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" - batch "k8s.io/api/batch/v1beta1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestGetCronJobListFromChannels(t *testing.T) { diff --git a/src/app/backend/resource/job/list.go b/src/app/backend/resource/job/list.go index 07c2e3ef8c78..c068047aec40 100644 --- a/src/app/backend/resource/job/list.go +++ b/src/app/backend/resource/job/list.go @@ -17,15 +17,16 @@ package job import ( "log" + batch "k8s.io/api/batch/v1" + v1 "k8s.io/api/core/v1" + client "k8s.io/client-go/kubernetes" + "github.com/kubernetes/dashboard/src/app/backend/api" "github.com/kubernetes/dashboard/src/app/backend/errors" metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" "github.com/kubernetes/dashboard/src/app/backend/resource/event" - batch "k8s.io/api/batch/v1" - v1 "k8s.io/api/core/v1" - client "k8s.io/client-go/kubernetes" ) // JobList contains a list of Jobs in the cluster.