github.com/caesarxuchao/heapster@v1.1.0/integration/heapster_api_test.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package integration
    16  
    17  import (
    18  	"encoding/json"
    19  	"flag"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/golang/glog"
    28  	"github.com/stretchr/testify/require"
    29  	api_v1 "k8s.io/heapster/metrics/api/v1/types"
    30  	metrics_api "k8s.io/heapster/metrics/apis/metrics/v1alpha1"
    31  	"k8s.io/heapster/metrics/core"
    32  	kube_api "k8s.io/kubernetes/pkg/api"
    33  	apiErrors "k8s.io/kubernetes/pkg/api/errors"
    34  	kube_api_unv "k8s.io/kubernetes/pkg/api/unversioned"
    35  	kube_v1 "k8s.io/kubernetes/pkg/api/v1"
    36  )
    37  
    38  const (
    39  	targetTags       = "kubernetes-minion"
    40  	heapsterBuildDir = "../deploy/docker"
    41  )
    42  
    43  var (
    44  	testZone               = flag.String("test_zone", "us-central1-b", "GCE zone where the test will be executed")
    45  	kubeVersions           = flag.String("kube_versions", "", "Comma separated list of kube versions to test against. By default will run the test against an existing cluster")
    46  	heapsterControllerFile = flag.String("heapster_controller", "../deploy/kube-config/standalone-test/heapster-controller.yaml", "Path to heapster replication controller file.")
    47  	heapsterServiceFile    = flag.String("heapster_service", "../deploy/kube-config/standalone-test/heapster-service.yaml", "Path to heapster service file.")
    48  	heapsterImage          = flag.String("heapster_image", "heapster:e2e_test", "heapster docker image that needs to be tested.")
    49  	avoidBuild             = flag.Bool("nobuild", false, "When true, a new heapster docker image will not be created and pushed to test cluster nodes.")
    50  	namespace              = flag.String("namespace", "heapster-e2e-tests", "namespace to be used for testing, it will be deleted at the beginning of the test if exists")
    51  	maxRetries             = flag.Int("retries", 20, "Number of attempts before failing this test.")
    52  	runForever             = flag.Bool("run_forever", false, "If true, the tests are run in a loop forever.")
    53  )
    54  
    55  func deleteAll(fm kubeFramework, ns string, service *kube_api.Service, rc *kube_api.ReplicationController) error {
    56  	glog.V(2).Infof("Deleting ns %s...", ns)
    57  	err := fm.DeleteNs(ns)
    58  	if err != nil {
    59  		glog.V(2).Infof("Failed to delete %s", ns)
    60  		return err
    61  	}
    62  	glog.V(2).Infof("Deleted ns %s.", ns)
    63  	return nil
    64  }
    65  
    66  func createAll(fm kubeFramework, ns string, service **kube_api.Service, rc **kube_api.ReplicationController) error {
    67  	glog.V(2).Infof("Creating ns %s...", ns)
    68  	namespace := kube_api.Namespace{
    69  		TypeMeta: kube_api_unv.TypeMeta{
    70  			Kind:       "Namespace",
    71  			APIVersion: "v1",
    72  		},
    73  		ObjectMeta: kube_api.ObjectMeta{
    74  			Name: ns,
    75  		},
    76  	}
    77  	if _, err := fm.CreateNs(&namespace); err != nil {
    78  		glog.V(2).Infof("Failed to create ns: %v", err)
    79  		return err
    80  	}
    81  
    82  	glog.V(2).Infof("Created ns %s.", ns)
    83  
    84  	glog.V(2).Infof("Creating rc %s/%s...", ns, (*rc).Name)
    85  	if newRc, err := fm.CreateRC(ns, *rc); err != nil {
    86  		glog.V(2).Infof("Failed to create rc: %v", err)
    87  		return err
    88  	} else {
    89  		*rc = newRc
    90  	}
    91  	glog.V(2).Infof("Created rc %s/%s.", ns, (*rc).Name)
    92  
    93  	glog.V(2).Infof("Creating service %s/%s...", ns, (*service).Name)
    94  	if newSvc, err := fm.CreateService(ns, *service); err != nil {
    95  		glog.V(2).Infof("Failed to create service: %v", err)
    96  		return err
    97  	} else {
    98  		*service = newSvc
    99  	}
   100  	glog.V(2).Infof("Created service %s/%s.", ns, (*service).Name)
   101  
   102  	return nil
   103  }
   104  
   105  func removeHeapsterImage(fm kubeFramework, zone string) error {
   106  	glog.V(2).Infof("Removing heapster image.")
   107  	if err := removeDockerImage(*heapsterImage); err != nil {
   108  		glog.Errorf("Failed to remove Heapster image: %v", err)
   109  	} else {
   110  		glog.V(2).Infof("Heapster image removed.")
   111  	}
   112  	if nodes, err := fm.GetNodes(); err == nil {
   113  		for _, node := range nodes {
   114  			host := strings.Split(node, ".")[0]
   115  			cleanupRemoteHost(host, zone)
   116  		}
   117  	} else {
   118  		glog.Errorf("failed to cleanup nodes - %v", err)
   119  	}
   120  	return nil
   121  }
   122  
   123  func buildAndPushHeapsterImage(hostnames []string, zone string) error {
   124  	glog.V(2).Info("Building and pushing Heapster image...")
   125  	curwd, err := os.Getwd()
   126  	if err != nil {
   127  		return err
   128  	}
   129  	if err := os.Chdir(heapsterBuildDir); err != nil {
   130  		return err
   131  	}
   132  	if err := buildDockerImage(*heapsterImage); err != nil {
   133  		return err
   134  	}
   135  	for _, host := range hostnames {
   136  		if err := copyDockerImage(*heapsterImage, host, zone); err != nil {
   137  			return err
   138  		}
   139  	}
   140  	glog.V(2).Info("Heapster image pushed.")
   141  	return os.Chdir(curwd)
   142  }
   143  
   144  func getHeapsterRcAndSvc(fm kubeFramework) (*kube_api.Service, *kube_api.ReplicationController, error) {
   145  	// Add test docker image
   146  	rc, err := fm.ParseRC(*heapsterControllerFile)
   147  	if err != nil {
   148  		return nil, nil, fmt.Errorf("failed to parse heapster controller - %v", err)
   149  	}
   150  	for i := range rc.Spec.Template.Spec.Containers {
   151  		rc.Spec.Template.Spec.Containers[i].Image = *heapsterImage
   152  		rc.Spec.Template.Spec.Containers[i].ImagePullPolicy = kube_api.PullNever
   153  		// increase logging level
   154  		rc.Spec.Template.Spec.Containers[i].Env = append(rc.Spec.Template.Spec.Containers[0].Env, kube_api.EnvVar{Name: "FLAGS", Value: "--vmodule=*=3"})
   155  	}
   156  
   157  	svc, err := fm.ParseService(*heapsterServiceFile)
   158  	if err != nil {
   159  		return nil, nil, fmt.Errorf("failed to parse heapster service - %v", err)
   160  	}
   161  
   162  	return svc, rc, nil
   163  }
   164  
   165  func buildAndPushDockerImages(fm kubeFramework, zone string) error {
   166  	if *avoidBuild {
   167  		return nil
   168  	}
   169  	nodes, err := fm.GetNodes()
   170  	if err != nil {
   171  		return err
   172  	}
   173  	hostnames := []string{}
   174  	for _, node := range nodes {
   175  		hostnames = append(hostnames, strings.Split(node, ".")[0])
   176  	}
   177  
   178  	return buildAndPushHeapsterImage(hostnames, zone)
   179  }
   180  
   181  const (
   182  	metricsEndpoint       = "/api/v1/metric-export"
   183  	metricsSchemaEndpoint = "/api/v1/metric-export-schema"
   184  )
   185  
   186  func getTimeseries(fm kubeFramework, svc *kube_api.Service) ([]*api_v1.Timeseries, error) {
   187  	body, err := fm.Client().Get().
   188  		Namespace(svc.Namespace).
   189  		Prefix("proxy").
   190  		Resource("services").
   191  		Name(svc.Name).
   192  		Suffix(metricsEndpoint).
   193  		Do().Raw()
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	var timeseries []*api_v1.Timeseries
   198  	if err := json.Unmarshal(body, &timeseries); err != nil {
   199  		glog.V(2).Infof("Timeseries error: %v %v", err, string(body))
   200  		return nil, err
   201  	}
   202  	return timeseries, nil
   203  }
   204  
   205  func getSchema(fm kubeFramework, svc *kube_api.Service) (*api_v1.TimeseriesSchema, error) {
   206  	body, err := fm.Client().Get().
   207  		Namespace(svc.Namespace).
   208  		Prefix("proxy").
   209  		Resource("services").
   210  		Name(svc.Name).
   211  		Suffix(metricsSchemaEndpoint).
   212  		Do().Raw()
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	var timeseriesSchema api_v1.TimeseriesSchema
   217  	if err := json.Unmarshal(body, &timeseriesSchema); err != nil {
   218  		glog.V(2).Infof("Metrics schema error: %v  %v", err, string(body))
   219  		return nil, err
   220  	}
   221  	return &timeseriesSchema, nil
   222  }
   223  
   224  var expectedSystemContainers = map[string]struct{}{
   225  	"machine":       {},
   226  	"kubelet":       {},
   227  	"kube-proxy":    {},
   228  	"system":        {},
   229  	"docker-daemon": {},
   230  }
   231  
   232  func isContainerBaseImageExpected(ts *api_v1.Timeseries) bool {
   233  	_, exists := expectedSystemContainers[ts.Labels[core.LabelContainerName.Key]]
   234  	return !exists
   235  }
   236  
   237  func runMetricExportTest(fm kubeFramework, svc *kube_api.Service) error {
   238  	expectedPods, err := fm.GetRunningPodNames()
   239  	if err != nil {
   240  		return err
   241  	}
   242  	glog.V(0).Infof("Expected pods: %v", expectedPods)
   243  
   244  	expectedNodes, err := fm.GetNodes()
   245  	if err != nil {
   246  		return err
   247  	}
   248  	glog.V(0).Infof("Expected nodes: %v", expectedNodes)
   249  
   250  	timeseries, err := getTimeseries(fm, svc)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	if len(timeseries) == 0 {
   255  		return fmt.Errorf("expected non zero timeseries")
   256  	}
   257  	schema, err := getSchema(fm, svc)
   258  	if err != nil {
   259  		return err
   260  	}
   261  	// Build a map of metric names to metric descriptors.
   262  	mdMap := map[string]*api_v1.MetricDescriptor{}
   263  	for idx := range schema.Metrics {
   264  		mdMap[schema.Metrics[idx].Name] = &schema.Metrics[idx]
   265  	}
   266  	actualPods := map[string]bool{}
   267  	actualNodes := map[string]bool{}
   268  	actualSystemContainers := map[string]map[string]struct{}{}
   269  	for _, ts := range timeseries {
   270  		// Verify the relevant labels are present.
   271  		// All common labels must be present.
   272  		podName, podMetric := ts.Labels[core.LabelPodName.Key]
   273  
   274  		for _, label := range core.CommonLabels() {
   275  			_, exists := ts.Labels[label.Key]
   276  			if !exists {
   277  				return fmt.Errorf("timeseries: %v does not contain common label: %v", ts, label)
   278  			}
   279  		}
   280  		if podMetric {
   281  			for _, label := range core.PodLabels() {
   282  				_, exists := ts.Labels[label.Key]
   283  				if !exists {
   284  					return fmt.Errorf("timeseries: %v does not contain pod label: %v", ts, label)
   285  				}
   286  			}
   287  		}
   288  
   289  		if podMetric {
   290  			actualPods[podName] = true
   291  			// Extra explicit check that the expecte metrics are there:
   292  			requiredLabels := []string{
   293  				core.LabelPodNamespaceUID.Key,
   294  				core.LabelPodId.Key,
   295  				core.LabelHostID.Key,
   296  				// container name is checked later
   297  			}
   298  			for _, label := range requiredLabels {
   299  				_, exists := ts.Labels[label]
   300  				if !exists {
   301  					return fmt.Errorf("timeseries: %v does not contain required label: %v", ts, label)
   302  				}
   303  			}
   304  
   305  		} else {
   306  			if cName, ok := ts.Labels[core.LabelContainerName.Key]; ok {
   307  				hostname, ok := ts.Labels[core.LabelHostname.Key]
   308  				if !ok {
   309  					return fmt.Errorf("hostname label missing on container %+v", ts)
   310  				}
   311  
   312  				if cName == "machine" {
   313  					actualNodes[hostname] = true
   314  				} else {
   315  					for _, label := range core.ContainerLabels() {
   316  						if label == core.LabelContainerBaseImage && !isContainerBaseImageExpected(ts) {
   317  							continue
   318  						}
   319  						_, exists := ts.Labels[label.Key]
   320  						if !exists {
   321  							return fmt.Errorf("timeseries: %v does not contain container label: %v", ts, label)
   322  						}
   323  					}
   324  				}
   325  
   326  				if _, exists := expectedSystemContainers[cName]; exists {
   327  					if actualSystemContainers[cName] == nil {
   328  						actualSystemContainers[cName] = map[string]struct{}{}
   329  					}
   330  					actualSystemContainers[cName][hostname] = struct{}{}
   331  				}
   332  			} else {
   333  				return fmt.Errorf("container_name label missing on timeseries - %v", ts)
   334  			}
   335  		}
   336  
   337  		// Explicitely check for resource id
   338  		explicitRequirement := map[string][]string{
   339  			core.MetricFilesystemUsage.MetricDescriptor.Name: {core.LabelResourceID.Key},
   340  			core.MetricFilesystemLimit.MetricDescriptor.Name: {core.LabelResourceID.Key},
   341  			core.MetricFilesystemAvailable.Name:              {core.LabelResourceID.Key}}
   342  
   343  		for metricName, points := range ts.Metrics {
   344  			md, exists := mdMap[metricName]
   345  			if !exists {
   346  				return fmt.Errorf("unexpected metric %q", metricName)
   347  			}
   348  
   349  			for _, point := range points {
   350  				for _, label := range md.Labels {
   351  					_, exists := point.Labels[label.Key]
   352  					if !exists {
   353  						return fmt.Errorf("metric %q point %v does not contain metric label: %v", metricName, point, label)
   354  					}
   355  				}
   356  			}
   357  
   358  			required := explicitRequirement[metricName]
   359  			for _, label := range required {
   360  				for _, point := range points {
   361  					_, exists := point.Labels[label]
   362  					if !exists {
   363  						return fmt.Errorf("metric %q point %v does not contain metric label: %v", metricName, point, label)
   364  					}
   365  
   366  				}
   367  			}
   368  		}
   369  	}
   370  	// Validate that system containers are running on all the nodes.
   371  	// This test could fail if one of the containers was down while the metrics sample was collected.
   372  	for cName, hosts := range actualSystemContainers {
   373  		for _, host := range expectedNodes {
   374  			if _, ok := hosts[host]; !ok {
   375  				return fmt.Errorf("System container %q not found on host: %q - %v", cName, host, actualSystemContainers)
   376  			}
   377  		}
   378  	}
   379  
   380  	if err := expectedItemsExist(expectedPods, actualPods); err != nil {
   381  		return fmt.Errorf("expected pods don't exist %v.\nExpected: %v\nActual:%v", err, expectedPods, actualPods)
   382  	}
   383  	if err := expectedItemsExist(expectedNodes, actualNodes); err != nil {
   384  		return fmt.Errorf("expected nodes don't exist %v.\nExpected: %v\nActual:%v", err, expectedNodes, actualNodes)
   385  	}
   386  
   387  	return nil
   388  }
   389  
   390  func expectedItemsExist(expectedItems []string, actualItems map[string]bool) error {
   391  	for _, item := range expectedItems {
   392  		if _, found := actualItems[item]; !found {
   393  			return fmt.Errorf("missing %s", item)
   394  		}
   395  	}
   396  	return nil
   397  }
   398  
   399  func getErrorCauses(err error) string {
   400  	serr, ok := err.(*apiErrors.StatusError)
   401  	if !ok {
   402  		return ""
   403  	}
   404  	var causes []string
   405  	for _, c := range serr.ErrStatus.Details.Causes {
   406  		causes = append(causes, c.Message)
   407  	}
   408  	return strings.Join(causes, ", ")
   409  }
   410  
   411  func getDataFromProxy(fm kubeFramework, svc *kube_api.Service, url string) ([]byte, error) {
   412  	glog.V(2).Infof("Querying heapster: %s", url)
   413  	return fm.Client().Get().
   414  		Namespace(svc.Namespace).
   415  		Prefix("proxy").
   416  		Resource("services").
   417  		Name(svc.Name).
   418  		Suffix(url).
   419  		Do().Raw()
   420  }
   421  
   422  func getMetricResultList(fm kubeFramework, svc *kube_api.Service, url string) (*api_v1.MetricResultList, error) {
   423  	body, err := getDataFromProxy(fm, svc, url)
   424  	if err != nil {
   425  		return nil, err
   426  	}
   427  	var data api_v1.MetricResultList
   428  	if err := json.Unmarshal(body, &data); err != nil {
   429  		glog.V(2).Infof("response body: %v", string(body))
   430  		return nil, err
   431  	}
   432  	if err := checkMetricResultListSanity(&data); err != nil {
   433  		return nil, err
   434  	}
   435  	return &data, nil
   436  }
   437  
   438  func getMetricResult(fm kubeFramework, svc *kube_api.Service, url string) (*api_v1.MetricResult, error) {
   439  	body, err := getDataFromProxy(fm, svc, url)
   440  	if err != nil {
   441  		return nil, err
   442  	}
   443  	var data api_v1.MetricResult
   444  	if err := json.Unmarshal(body, &data); err != nil {
   445  		glog.V(2).Infof("response body: %v", string(body))
   446  		return nil, err
   447  	}
   448  	if err := checkMetricResultSanity(&data); err != nil {
   449  		return nil, err
   450  	}
   451  	return &data, nil
   452  }
   453  
   454  func getStringResult(fm kubeFramework, svc *kube_api.Service, url string) ([]string, error) {
   455  	body, err := getDataFromProxy(fm, svc, url)
   456  	if err != nil {
   457  		return nil, err
   458  	}
   459  	var data []string
   460  	if err := json.Unmarshal(body, &data); err != nil {
   461  		glog.V(2).Infof("response body: %v", string(body))
   462  		return nil, err
   463  	}
   464  	if len(data) == 0 {
   465  		return nil, fmt.Errorf("empty string array")
   466  	}
   467  	return data, nil
   468  }
   469  
   470  func checkMetricResultSanity(metrics *api_v1.MetricResult) error {
   471  	bytes, err := json.Marshal(*metrics)
   472  	if err != nil {
   473  		return err
   474  	}
   475  	stringVersion := string(bytes)
   476  
   477  	if len(metrics.Metrics) == 0 {
   478  		return fmt.Errorf("empty metrics: %s", stringVersion)
   479  	}
   480  	// There should be recent metrics in the response.
   481  	if time.Now().Sub(metrics.LatestTimestamp).Seconds() > 120 {
   482  		return fmt.Errorf("corrupted last timestamp: %s", stringVersion)
   483  	}
   484  	// Metrics don't have to be sorted, so the oldest one can be first.
   485  	if time.Now().Sub(metrics.Metrics[0].Timestamp).Hours() > 1 {
   486  		return fmt.Errorf("corrupted timestamp: %s", stringVersion)
   487  	}
   488  	if metrics.Metrics[0].Value > 10000 {
   489  		return fmt.Errorf("value too big: %s", stringVersion)
   490  	}
   491  	return nil
   492  }
   493  
   494  func checkMetricResultListSanity(metrics *api_v1.MetricResultList) error {
   495  	if len(metrics.Items) == 0 {
   496  		return fmt.Errorf("empty metrics")
   497  	}
   498  	for _, item := range metrics.Items {
   499  		err := checkMetricResultSanity(&item)
   500  		if err != nil {
   501  			return err
   502  		}
   503  	}
   504  	return nil
   505  }
   506  
   507  func runModelTest(fm kubeFramework, svc *kube_api.Service) error {
   508  	podList, err := fm.GetRunningPods()
   509  	if err != nil {
   510  		return err
   511  	}
   512  	if len(podList) == 0 {
   513  		return fmt.Errorf("empty pod list")
   514  	}
   515  	nodeList, err := fm.GetNodes()
   516  	if err != nil {
   517  		return err
   518  	}
   519  	if len(nodeList) == 0 {
   520  		return fmt.Errorf(("empty node list"))
   521  	}
   522  	podNamesList := make([]string, 0, len(podList))
   523  	for _, pod := range podList {
   524  		podNamesList = append(podNamesList, fmt.Sprintf("%s/%s", pod.Namespace, pod.Name))
   525  	}
   526  
   527  	glog.V(0).Infof("Expected nodes:\n%s", strings.Join(nodeList, "\n"))
   528  	glog.V(0).Infof("Expected pods:\n%s", strings.Join(podNamesList, "\n"))
   529  	allkeys, err := getStringResult(fm, svc, "/api/v1/model/debug/allkeys")
   530  	if err != nil {
   531  		return fmt.Errorf("Failed to get debug information about keys: %v", err)
   532  	}
   533  	glog.V(0).Infof("Available Heapster metric sets:\n%s", strings.Join(allkeys, "\n"))
   534  
   535  	metricUrlsToCheck := []string{}
   536  	batchMetricsUrlsToCheck := []string{}
   537  	stringUrlsToCheck := []string{}
   538  
   539  	/* TODO: enable once cluster aggregator is added.
   540  	   metricUrlsToCheck = append(metricUrlsToCheck,
   541  	   fmt.Sprintf("/api/v1/model/metrics/%s", "cpu-usage"),
   542  	)
   543  	*/
   544  
   545  	/* TODO: add once Cluster metrics aggregator is added.
   546  	   "/api/v1/model/metrics",
   547  	   "/api/v1/model/"
   548  	*/
   549  	stringUrlsToCheck = append(stringUrlsToCheck)
   550  
   551  	for _, node := range nodeList {
   552  		metricUrlsToCheck = append(metricUrlsToCheck,
   553  			fmt.Sprintf("/api/v1/model/nodes/%s/metrics/%s", node, "cpu/usage_rate"),
   554  			fmt.Sprintf("/api/v1/model/nodes/%s/metrics/%s", node, "cpu-usage"),
   555  		)
   556  
   557  		stringUrlsToCheck = append(stringUrlsToCheck,
   558  			fmt.Sprintf("/api/v1/model/nodes/%s/metrics", node),
   559  		)
   560  	}
   561  
   562  	for _, pod := range podList {
   563  		containerName := pod.Spec.Containers[0].Name
   564  
   565  		metricUrlsToCheck = append(metricUrlsToCheck,
   566  			fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics/%s", pod.Namespace, pod.Name, "cpu/usage_rate"),
   567  			fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics/%s", pod.Namespace, pod.Name, "cpu-usage"),
   568  			fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics/%s", pod.Namespace, pod.Name, containerName, "cpu/usage_rate"),
   569  			fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics/%s", pod.Namespace, pod.Name, containerName, "cpu-usage"),
   570  			fmt.Sprintf("/api/v1/model/namespaces/%s/metrics/%s", pod.Namespace, "cpu/usage_rate"),
   571  			fmt.Sprintf("/api/v1/model/namespaces/%s/metrics/%s", pod.Namespace, "cpu-usage"),
   572  		)
   573  
   574  		batchMetricsUrlsToCheck = append(batchMetricsUrlsToCheck,
   575  			fmt.Sprintf("/api/v1/model/namespaces/%s/pod-list/%s,%s/metrics/%s", pod.Namespace, pod.Name, pod.Name, "cpu/usage_rate"),
   576  			fmt.Sprintf("/api/v1/model/namespaces/%s/pod-list/%s,%s/metrics/%s", pod.Namespace, pod.Name, pod.Name, "cpu-usage"),
   577  		)
   578  
   579  		stringUrlsToCheck = append(stringUrlsToCheck,
   580  			fmt.Sprintf("/api/v1/model/namespaces/%s/metrics", pod.Namespace),
   581  			fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics", pod.Namespace, pod.Name),
   582  			fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics", pod.Namespace, pod.Name, containerName),
   583  		)
   584  	}
   585  
   586  	for _, url := range metricUrlsToCheck {
   587  		_, err := getMetricResult(fm, svc, url)
   588  		if err != nil {
   589  			return fmt.Errorf("error while querying %s: %v", url, err)
   590  		}
   591  	}
   592  
   593  	for _, url := range batchMetricsUrlsToCheck {
   594  		_, err := getMetricResultList(fm, svc, url)
   595  		if err != nil {
   596  			return fmt.Errorf("error while querying %s: %v", url, err)
   597  		}
   598  	}
   599  
   600  	for _, url := range stringUrlsToCheck {
   601  		_, err := getStringResult(fm, svc, url)
   602  		if err != nil {
   603  			return fmt.Errorf("error while querying %s: %v", url, err)
   604  		}
   605  	}
   606  	return nil
   607  }
   608  
   609  func checkUsage(res kube_v1.ResourceList) error {
   610  	if _, found := res[kube_v1.ResourceCPU]; !found {
   611  		return fmt.Errorf("Cpu not found")
   612  	}
   613  	if _, found := res[kube_v1.ResourceMemory]; !found {
   614  		return fmt.Errorf("Memory not found")
   615  	}
   616  	return nil
   617  }
   618  
   619  func getPodMetrics(fm kubeFramework, svc *kube_api.Service, pod kube_api.Pod) (*metrics_api.PodMetrics, error) {
   620  	url := "apis/metrics/v1alpha1/namespaces/" + pod.Namespace + "/pods/" + pod.Name
   621  	body, err := getDataFromProxy(fm, svc, url)
   622  	if err != nil {
   623  		return nil, err
   624  	}
   625  	var data metrics_api.PodMetrics
   626  	if err := json.Unmarshal(body, &data); err != nil {
   627  		glog.V(2).Infof("response body: %v", string(body))
   628  		return nil, err
   629  	}
   630  	return &data, nil
   631  }
   632  
   633  func checkPodsInMetricsApi(fm kubeFramework, svc *kube_api.Service, pods []kube_api.Pod) error {
   634  	for _, pod := range pods {
   635  		metrics, err := getPodMetrics(fm, svc, pod)
   636  		if err != nil {
   637  			return err
   638  		}
   639  		if metrics.Name != pod.Name {
   640  			return fmt.Errorf("Wrong pod name: expected %v, got %v", pod.Name, metrics.Name)
   641  		}
   642  		if metrics.Namespace != pod.Namespace {
   643  			return fmt.Errorf("Wrong pod namespace: expected %v, got %v", pod.Namespace, metrics.Namespace)
   644  		}
   645  		if len(pod.Spec.Containers) != len(metrics.Containers) {
   646  			return fmt.Errorf("Wrong number of containers in returned metrics: expected %v, got %v", len(pod.Spec.Containers), len(metrics.Containers))
   647  		}
   648  		for _, c := range metrics.Containers {
   649  			if err := checkUsage(c.Usage); err != nil {
   650  				return err
   651  			}
   652  		}
   653  	}
   654  	return nil
   655  }
   656  
   657  func getNodeMetrics(fm kubeFramework, svc *kube_api.Service, node string) (*metrics_api.NodeMetrics, error) {
   658  	url := "apis/metrics/v1alpha1/nodes/" + node
   659  	body, err := getDataFromProxy(fm, svc, url)
   660  	if err != nil {
   661  		return nil, err
   662  	}
   663  	var data metrics_api.NodeMetrics
   664  	if err := json.Unmarshal(body, &data); err != nil {
   665  		glog.V(2).Infof("response body: %v", string(body))
   666  		return nil, err
   667  	}
   668  	return &data, nil
   669  }
   670  
   671  func checkNodesInMetricsApi(fm kubeFramework, svc *kube_api.Service, nodes []string) error {
   672  	for _, node := range nodes {
   673  		metrics, err := getNodeMetrics(fm, svc, node)
   674  		if err != nil {
   675  			return err
   676  		}
   677  		if metrics.Name != node {
   678  			return fmt.Errorf("Wrong node name: expected %v, got %v", node, metrics.Name)
   679  		}
   680  		if err := checkUsage(metrics.Usage); err != nil {
   681  			return err
   682  		}
   683  	}
   684  	return nil
   685  }
   686  
   687  func runMetricsApiTest(fm kubeFramework, svc *kube_api.Service) error {
   688  	podList, err := fm.GetRunningPods()
   689  	if err != nil {
   690  		return err
   691  	}
   692  	if len(podList) == 0 {
   693  		return fmt.Errorf("empty pod list")
   694  	}
   695  	if err := checkPodsInMetricsApi(fm, svc, podList); err != nil {
   696  		return err
   697  	}
   698  
   699  	nodeList, err := fm.GetNodes()
   700  	if err != nil {
   701  		return err
   702  	}
   703  	if len(nodeList) == 0 {
   704  		return fmt.Errorf(("empty node list"))
   705  	}
   706  	return checkNodesInMetricsApi(fm, svc, nodeList)
   707  }
   708  
   709  func apiTest(kubeVersion string, zone string) error {
   710  	fm, err := newKubeFramework(kubeVersion)
   711  	if err != nil {
   712  		return err
   713  	}
   714  	if err := buildAndPushDockerImages(fm, zone); err != nil {
   715  		return err
   716  	}
   717  	// Create heapster pod and service.
   718  	svc, rc, err := getHeapsterRcAndSvc(fm)
   719  	if err != nil {
   720  		return err
   721  	}
   722  	ns := *namespace
   723  	if err := deleteAll(fm, ns, svc, rc); err != nil {
   724  		return err
   725  	}
   726  	if err := createAll(fm, ns, &svc, &rc); err != nil {
   727  		return err
   728  	}
   729  	if err := fm.WaitUntilPodRunning(ns, rc.Spec.Template.Labels, time.Minute); err != nil {
   730  		return err
   731  	}
   732  	if err := fm.WaitUntilServiceActive(svc, time.Minute); err != nil {
   733  		return err
   734  	}
   735  	testFuncs := []func() error{
   736  		func() error {
   737  			glog.V(2).Infof("Heapster metric export test...")
   738  			err := runMetricExportTest(fm, svc)
   739  			if err == nil {
   740  				glog.V(2).Infof("Heapster metric export test: OK")
   741  			} else {
   742  				glog.V(2).Infof("Heapster metric export test error: %v", err)
   743  			}
   744  			return err
   745  		},
   746  		func() error {
   747  			glog.V(2).Infof("Model test")
   748  			err := runModelTest(fm, svc)
   749  			if err == nil {
   750  				glog.V(2).Infof("Model test: OK")
   751  			} else {
   752  				glog.V(2).Infof("Model test error: %v", err)
   753  			}
   754  			return err
   755  		},
   756  		func() error {
   757  			glog.V(2).Infof("Metrics API test")
   758  			err := runMetricsApiTest(fm, svc)
   759  			if err == nil {
   760  				glog.V(2).Infof("Metrics API test: OK")
   761  			} else {
   762  				glog.V(2).Infof("Metrics API test error: %v", err)
   763  			}
   764  			return err
   765  		},
   766  	}
   767  	attempts := *maxRetries
   768  	glog.Infof("Starting tests")
   769  	for {
   770  		var err error
   771  		for _, testFunc := range testFuncs {
   772  			if err = testFunc(); err != nil {
   773  				break
   774  			}
   775  		}
   776  		if *runForever {
   777  			continue
   778  		}
   779  		if err == nil {
   780  			glog.V(2).Infof("All tests passed.")
   781  			break
   782  		}
   783  		if attempts == 0 {
   784  			glog.V(2).Info("Too many attempts.")
   785  			return err
   786  		}
   787  		glog.V(2).Infof("Some tests failed. Retrying.")
   788  		attempts--
   789  		time.Sleep(time.Second * 10)
   790  	}
   791  	deleteAll(fm, ns, svc, rc)
   792  	removeHeapsterImage(fm, zone)
   793  	return nil
   794  }
   795  
   796  func runApiTest() error {
   797  	tempDir, err := ioutil.TempDir("", "deploy")
   798  	if err != nil {
   799  		return nil
   800  	}
   801  	defer os.RemoveAll(tempDir)
   802  	if *kubeVersions == "" {
   803  		return apiTest("", *testZone)
   804  	}
   805  	kubeVersionsList := strings.Split(*kubeVersions, ",")
   806  	for _, kubeVersion := range kubeVersionsList {
   807  		if err := apiTest(kubeVersion, *testZone); err != nil {
   808  			return err
   809  		}
   810  	}
   811  	return nil
   812  }
   813  
   814  func TestHeapster(t *testing.T) {
   815  	if testing.Short() {
   816  		t.Skip("skipping heapster kubernetes integration test.")
   817  	}
   818  	require.NoError(t, runApiTest())
   819  }