Repository: kubernetes/dashboard
Pull request #7189 (2022-06-16 06:02:37)

fix: fix panic while RsaKeyHolder secret is nil

linux-foundation-easycla[bot]:  <a href="https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/18706487/44350607/7189/#/?version=2"><img src="https://s3.amazonaws.com/cla-project-logo-prod/cla-not-signed.svg" alt="CLA Not Signed" align="left" height="28" width="328"></a><br/><br /><ul><li><a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/18706487/44350607/7189/#/?version=2' target='_blank'>:x:</a> - login: @ismdeep / name: L. Jiang . The commit (6e55f8b8b366e2b29cfeaeec503fced740c5c908) is not authorized under a signed CLA. <a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/18706487/44350607/7189/#/?version=2' target='_blank'>Please click here to be authorized</a>. For further assistance with EasyCLA, <a href='https://jira.linuxfoundation.org/servicedesk/customer/portal/4' target='_blank'>please submit a support request ticket</a>.</li></ul>
k8s-ci-robot: Welcome @ismdeep! <br><br>It looks like this is your first PR to <a href='https://github.com/kubernetes/dashboard'>kubernetes/dashboard</a> 🎉. Please refer to our [pull request process documentation](https://git.k8s.io/community/contributors/guide/pull-requests.md) to help your PR have a smooth ride to approval. <br><br>You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. [Here is the bot commands documentation](https://go.k8s.io/bot-commands). <br><br>You can also check if kubernetes/dashboard has [its own contribution guidelines](https://github.com/kubernetes/dashboard/tree/master/CONTRIBUTING.md). <br><br>You may want to refer to our [testing guide](https://git.k8s.io/community/contributors/devel/sig-testing/testing.md) if you run into trouble with your tests not passing. <br><br>If you are having difficulty getting your pull request seen, please follow the [recommended escalation practices](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#why-is-my-pull-request-not-getting-reviewed). Also, for tips and tricks in the contribution process you may want to read the [Kubernetes contributor cheat sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md). We want to make sure your contribution gets all the attention it needs! <br><br>Thank you, and welcome to Kubernetes. :smiley:
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/dashboard/pull/7189#" title="Author self-approved">ismdeep</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **floreks** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @floreks` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fdashboard).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes/dashboard/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["floreks"]} -->

Patch:
diff --git a/docs/common/dashboard-arguments.md b/docs/common/dashboard-arguments.md
index 818b90ccc..b55b0cb84 100644
--- a/docs/common/dashboard-arguments.md
+++ b/docs/common/dashboard-arguments.md
@@ -15,7 +15,7 @@ Dashboard container accepts multiple arguments that can be used to customize it
 | tls-key-file                | -                  | File containing the default x509 private key matching --tls-cert-file.                                                                                                                                                                                                                                    |
 | auto-generate-certificates  | false              | When set to true, Dashboard will automatically generate certificates used to serve HTTPS.                                                                                                                                                                                                                 |
 | apiserver-host              | -                  | The address of the Kubernetes Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8080. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and local discovery is attempted.                                                       |
-| api-log-level               | INFO               | Level of API request logging. Should be one of 'INFO\                                                                                                                                                                                                                                                     |NONE\|DEBUG'. |
+| api-log-level               | INFO               | Level of API request logging. Should be one of `INFO\|NONE\|DEBUG`. |
 | heapster-host               | -                  | The address of the Heapster Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8082. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and service proxy will be used.                                                           |
 | sidecar-host                | -                  | The address of the Sidecar Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8000. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and service proxy will be used.                                                            |
 | metrics-provider            | sidecar            | Select provider type for metrics. 'none' will not check metrics.                                                                                                                                                                                                                                          |
@@ -29,7 +29,7 @@ Dashboard container accepts multiple arguments that can be used to customize it
 | disable-settings-authorizer | false              | When enabled, Dashboard settings page will not require user to be logged in and authorized to access settings page.                                                                                                                                                                                       |
 | locale-config               | ./locale_conf.json | File containing the configuration of locales.                                                                                                                                                                                                                                                             |
 | system-banner               | -                  | When non-empty displays message to Dashboard users. Accepts simple HTML tags.                                                                                                                                                                                                                             |
-| system-banner-severity      | INFO               | Severity of system banner. Should be one of 'INFO\                                                                                                                                                                                                                                                        |WARNING\|ERROR'. |
+| system-banner-severity      | INFO               | Severity of system banner. Should be one of `INFO\|WARNING\|ERROR`. |
 
 ----
 _Copyright 2019 [The Kubernetes Dashboard Authors](https://github.com/kubernetes/dashboard/graphs/contributors)_
diff --git a/go.mod b/go.mod
index 4db4c1c04..5c8630d9f 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 90eccf618..343a46ced 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/auth/jwe/keyholder.go b/src/app/backend/auth/jwe/keyholder.go
index 6f8068dcb..86b901f4c 100644
--- a/src/app/backend/auth/jwe/keyholder.go
+++ b/src/app/backend/auth/jwe/keyholder.go
@@ -87,7 +87,10 @@ func (self *rsaKeyHolder) Refresh() {
 // Handler function executed by synchronizer used to store encryption key. It is called whenever watched object
 // is created or updated.
 func (self *rsaKeyHolder) update(obj runtime.Object) {
-	secret := obj.(*v1.Secret)
+	secret, ok := obj.(*v1.Secret)
+	if !ok {
+		return
+	}
 	priv, err := ParseRSAKey(string(secret.Data[holderMapKeyEntry]), string(secret.Data[holderMapCertEntry]))
 	if err != nil {
 		// Secret was probably tampered with. Update it based on local key.
diff --git a/src/app/backend/dashboard.go b/src/app/backend/dashboard.go
index a9c926f31..42c3a92ce 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 f320273e7..281f1c19a 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/handler/terminal.go b/src/app/backend/handler/terminal.go
index a3e18981b..9fc066e37 100755
--- a/src/app/backend/handler/terminal.go
+++ b/src/app/backend/handler/terminal.go
@@ -48,7 +48,6 @@ type TerminalSession struct {
 	bound         chan error
 	sockJSSession sockjs.Session
 	sizeChan      chan remotecommand.TerminalSize
-	doneChan      chan struct{}
 }
 
 // TerminalMessage is the messaging protocol between ShellController and TerminalSession.
@@ -65,15 +64,14 @@ type TerminalMessage struct {
 	Rows, Cols          uint16
 }
 
-// TerminalSize handles pty->process resize events
+// Next handles pty->process resize events
 // Called in a loop from remotecommand as long as the process is running
 func (t TerminalSession) Next() *remotecommand.TerminalSize {
-	select {
-	case size := <-t.sizeChan:
-		return &size
-	case <-t.doneChan:
+	size := <-t.sizeChan
+	if size.Height == 0 && size.Width == 0 {
 		return nil
 	}
+	return &size
 }
 
 // Read handles pty->process messages (stdin, resize)
@@ -161,11 +159,12 @@ func (sm *SessionMap) Set(sessionId string, session TerminalSession) {
 func (sm *SessionMap) Close(sessionId string, status uint32, reason string) {
 	sm.Lock.Lock()
 	defer sm.Lock.Unlock()
-	err := sm.Sessions[sessionId].sockJSSession.Close(status, reason)
+	ses := sm.Sessions[sessionId]
+	err := ses.sockJSSession.Close(status, reason)
 	if err != nil {
 		log.Println(err)
 	}
-
+	close(ses.sizeChan)
 	delete(sm.Sessions, sessionId)
 }
 
diff --git a/src/app/backend/health/api/types.go b/src/app/backend/health/api/types.go
new file mode 100644
index 000000000..b514aadb1
--- /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 000000000..98852cb75
--- /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 000000000..e353ab90d
--- /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 289f54954..727859b46 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/metric/heapster/client.go b/src/app/backend/integration/metric/heapster/client.go
deleted file mode 100644
index cbe60324d..000000000
--- 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/common.go b/src/app/backend/integration/metric/heapster/common.go
deleted file mode 100644
index e8e2867d6..000000000
--- 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 68d73b7a1..000000000
--- 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 7a142b646..000000000
--- 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 74648e4a2..000000000
--- 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/manager.go b/src/app/backend/integration/metric/manager.go
index 2824d71ca..591652f09 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/resource/common/resourcechannels.go b/src/app/backend/resource/common/resourcechannels.go
index 32507b303..8d72dbfba 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 becbfb5ef..d0f55ef38 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 8becbe6c8..c41bcdfac 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/events.go b/src/app/backend/resource/cronjob/events.go
index bd13d936f..f062c33c7 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/jobs.go b/src/app/backend/resource/cronjob/jobs.go
index 7c4aef042..9731c66ed 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/list.go b/src/app/backend/resource/cronjob/list.go
index b7f88bee1..525bf5fa4 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/job/list.go b/src/app/backend/resource/job/list.go
index 07c2e3ef8..c068047ae 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.


Test patch:
diff --git a/src/app/backend/auth/basic_test.go b/src/app/backend/auth/basic_test.go
new file mode 100644
index 000000000..9c7451114
--- /dev/null
+++ b/src/app/backend/auth/basic_test.go
@@ -0,0 +1,19 @@
+package auth
+
+import (
+	authAPI "github.com/kubernetes/dashboard/src/app/backend/auth/api"
+	"testing"
+)
+
+func TestNewBasicAuthenticator(t *testing.T) {
+	auth := NewBasicAuthenticator(&authAPI.LoginSpec{
+		Username:   "username",
+		Password:   "password",
+		Token:      "",
+		KubeConfig: "",
+	})
+
+	if _, err := auth.GetAuthInfo(); err != nil {
+		t.Errorf("Failed: %v", err)
+	}
+}
diff --git a/src/app/backend/auth/jwe/keyholder_test.go b/src/app/backend/auth/jwe/keyholder_test.go
index af7ebe05b..71ed5fd28 100644
--- a/src/app/backend/auth/jwe/keyholder_test.go
+++ b/src/app/backend/auth/jwe/keyholder_test.go
@@ -47,3 +47,8 @@ func TestRsaKeyHolder_Key(t *testing.T) {
 		t.Fatalf("Key(): Expected key not to be nil")
 	}
 }
+
+func TestRsaKeyHolder_Refresh(t *testing.T) {
+	holder := getKeyHolder()
+	holder.Refresh()
+}
diff --git a/src/app/backend/integration/manager_test.go b/src/app/backend/integration/manager_test.go
index fc0cfb64b..f79a567ff 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_test.go b/src/app/backend/integration/metric/heapster/client_test.go
deleted file mode 100644
index 28b8cf082..000000000
--- 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/selector_test.go b/src/app/backend/integration/metric/heapster/selector_test.go
deleted file mode 100644
index 98f2a731b..000000000
--- 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_test.go b/src/app/backend/integration/metric/manager_test.go
index 678d8523b..292ad2306 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/cronjob/detail_test.go b/src/app/backend/resource/cronjob/detail_test.go
index 09dbd69cc..5918cc7c1 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_test.go b/src/app/backend/resource/cronjob/events_test.go
index 0bf93b24c..394a30fa3 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_test.go b/src/app/backend/resource/cronjob/jobs_test.go
index 3ecc50864..09e702207 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_test.go b/src/app/backend/resource/cronjob/list_test.go
index 0e0238708..5935839a4 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) {