github.com/galamsiva2020/kubernetes-heapster-monitoring@v0.0.0-20210823134957-3c1baa7c1e70/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 kube_v1 "k8s.io/api/core/v1" 30 apiErrors "k8s.io/apimachinery/pkg/api/errors" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/labels" 33 "k8s.io/apimachinery/pkg/util/sets" 34 api_v1 "k8s.io/heapster/metrics/api/v1/types" 35 "k8s.io/heapster/metrics/core" 36 metrics_api "k8s.io/metrics/pkg/apis/metrics/v1alpha1" 37 ) 38 39 const ( 40 targetTags = "kubernetes-minion" 41 heapsterBuildDir = "../" 42 ) 43 44 var ( 45 testZone = flag.String("test_zone", "us-central1-b", "GCE zone where the test will be executed") 46 kubeVersions = flag.String("kube_versions", "", "Comma separated list of kube versions to test against. By default will run the test against an existing cluster") 47 heapsterControllerFile = flag.String("heapster_controller", "../deploy/kube-config/standalone-test/heapster-controller.yaml", "Path to heapster replication controller file.") 48 heapsterServiceFile = flag.String("heapster_service", "../deploy/kube-config/standalone-test/heapster-service.yaml", "Path to heapster service file.") 49 heapsterRBACFile = flag.String("heapster_rbac", "../deploy/kube-config/standalone-test/heapster-rbac.yaml", "Path to heapster rbac file.") 50 heapsterServiceAccountFile = flag.String("heapster_service_account", "../deploy/kube-config/standalone-test/heapster-service-account.yaml", "Path to heapster service account file.") 51 heapsterImage = flag.String("heapster_image", "heapster:e2e_test", "heapster docker image that needs to be tested.") 52 avoidBuild = flag.Bool("nobuild", false, "When true, a new heapster docker image will not be created and pushed to test cluster nodes.") 53 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") 54 maxRetries = flag.Int("retries", 20, "Number of attempts before failing this test.") 55 runForever = flag.Bool("run_forever", false, "If true, the tests are run in a loop forever.") 56 testUser = flag.String("test_user", "", "GCE user when copy file to host") 57 ) 58 59 func deleteAll(fm kubeFramework, ns string, service *kube_v1.Service, rc *kube_v1.ReplicationController) error { 60 glog.V(2).Infof("Deleting ns %s...", ns) 61 err := fm.DeleteNs(ns) 62 if err != nil { 63 glog.V(2).Infof("Failed to delete %s", ns) 64 return err 65 } 66 glog.V(2).Infof("Deleted ns %s.", ns) 67 return nil 68 } 69 70 func createAll(fm kubeFramework, ns string, service **kube_v1.Service, rc **kube_v1.ReplicationController) error { 71 glog.V(2).Infof("Creating ns %s...", ns) 72 namespace := kube_v1.Namespace{ 73 TypeMeta: metav1.TypeMeta{ 74 Kind: "Namespace", 75 APIVersion: "v1", 76 }, 77 ObjectMeta: metav1.ObjectMeta{ 78 Name: ns, 79 }, 80 } 81 if _, err := fm.CreateNs(&namespace); err != nil { 82 glog.V(2).Infof("Failed to create ns: %v", err) 83 return err 84 } 85 86 glog.V(2).Infof("Created ns %s.", ns) 87 88 glog.V(2).Infof("Creating rc %s/%s...", ns, (*rc).Name) 89 if newRc, err := fm.CreateRC(ns, *rc); err != nil { 90 glog.V(2).Infof("Failed to create rc: %v", err) 91 return err 92 } else { 93 *rc = newRc 94 } 95 glog.V(2).Infof("Created rc %s/%s.", ns, (*rc).Name) 96 97 glog.V(2).Infof("Creating service %s/%s...", ns, (*service).Name) 98 if newSvc, err := fm.CreateService(ns, *service); err != nil { 99 glog.V(2).Infof("Failed to create service: %v", err) 100 return err 101 } else { 102 *service = newSvc 103 } 104 glog.V(2).Infof("Created service %s/%s.", ns, (*service).Name) 105 106 return nil 107 } 108 109 func removeHeapsterImage(fm kubeFramework, zone string) error { 110 glog.V(2).Infof("Removing heapster image.") 111 if err := removeDockerImage(*heapsterImage); err != nil { 112 glog.Errorf("Failed to remove Heapster image: %v", err) 113 } else { 114 glog.V(2).Infof("Heapster image removed.") 115 } 116 if nodes, err := fm.GetNodeNames(); err == nil { 117 for _, node := range nodes { 118 host := strings.Split(node, ".")[0] 119 cleanupRemoteHost(host, zone) 120 } 121 } else { 122 glog.Errorf("failed to cleanup nodes - %v", err) 123 } 124 return nil 125 } 126 127 func buildAndPushHeapsterImage(hostnames []string, zone string) error { 128 glog.V(2).Info("Building and pushing Heapster image...") 129 curwd, err := os.Getwd() 130 if err != nil { 131 return err 132 } 133 if err := os.Chdir(heapsterBuildDir); err != nil { 134 return err 135 } 136 if err := buildDockerImage(*heapsterImage); err != nil { 137 return err 138 } 139 for _, host := range hostnames { 140 if err := copyDockerImage(*heapsterImage, host, zone); err != nil { 141 return err 142 } 143 } 144 glog.V(2).Info("Heapster image pushed.") 145 return os.Chdir(curwd) 146 } 147 148 func getHeapsterRcAndSvc(fm kubeFramework) (*kube_v1.Service, *kube_v1.ReplicationController, error) { 149 // Add test docker image 150 rc, err := fm.ParseRC(*heapsterControllerFile) 151 if err != nil { 152 return nil, nil, fmt.Errorf("failed to parse heapster controller - %v", err) 153 } 154 for i := range rc.Spec.Template.Spec.Containers { 155 rc.Spec.Template.Spec.Containers[i].Image = *heapsterImage 156 rc.Spec.Template.Spec.Containers[i].ImagePullPolicy = kube_v1.PullNever 157 // increase logging level 158 rc.Spec.Template.Spec.Containers[i].Env = append(rc.Spec.Template.Spec.Containers[0].Env, kube_v1.EnvVar{Name: "FLAGS", Value: "--vmodule=*=3"}) 159 } 160 161 svc, err := fm.ParseService(*heapsterServiceFile) 162 if err != nil { 163 return nil, nil, fmt.Errorf("failed to parse heapster service - %v", err) 164 } 165 166 return svc, rc, nil 167 } 168 169 func setupHeapsterServiceAccount(fm kubeFramework) error { 170 sa, err := fm.ParseServiceAccount(*heapsterServiceAccountFile) 171 if err != nil { 172 return fmt.Errorf("failed to parse heapster serviceaccount - %v", err) 173 } 174 175 glog.V(2).Infof("Creating serviceaccount %s/%s...", (*sa).Namespace, (*sa).Name) 176 if err := fm.CreateServiceAccount(sa); err != nil { 177 glog.V(2).Infof("Failed to create serviceaccount: %v", err) 178 return err 179 } 180 181 return nil 182 } 183 184 func setupHeapsterRBAC(fm kubeFramework) error { 185 rbac, err := fm.ParseRBAC(*heapsterRBACFile) 186 if err != nil { 187 return fmt.Errorf("failed to parse heapster rbac - %v", err) 188 } 189 190 glog.V(2).Infof("Creating rbac %s...", (*rbac).Name) 191 if err := fm.CreateRBAC(rbac); err != nil { 192 glog.V(2).Infof("Failed to create rbac: %v", err) 193 return err 194 } 195 196 return nil 197 } 198 199 func buildAndPushDockerImages(fm kubeFramework, zone string) error { 200 if *avoidBuild { 201 return nil 202 } 203 nodes, err := fm.GetNodeNames() 204 if err != nil { 205 return err 206 } 207 hostnames := []string{} 208 for _, node := range nodes { 209 nodeHost := strings.Split(node, ".")[0] 210 if *testUser != "" { 211 nodeHost = fmt.Sprintf("%s@%s", *testUser, nodeHost) 212 } 213 hostnames = append(hostnames, nodeHost) 214 } 215 216 return buildAndPushHeapsterImage(hostnames, zone) 217 } 218 219 const ( 220 metricsEndpoint = "/api/v1/metric-export" 221 metricsSchemaEndpoint = "/api/v1/metric-export-schema" 222 ) 223 224 func getTimeseries(fm kubeFramework, svc *kube_v1.Service) ([]*api_v1.Timeseries, error) { 225 body, err := fm.Client().CoreV1().RESTClient().Get(). 226 Namespace(svc.Namespace). 227 Prefix("proxy"). 228 Resource("services"). 229 Name(svc.Name). 230 Suffix(metricsEndpoint). 231 Do().Raw() 232 if err != nil { 233 return nil, err 234 } 235 var timeseries []*api_v1.Timeseries 236 if err := json.Unmarshal(body, ×eries); err != nil { 237 glog.V(2).Infof("Timeseries error: %v %v", err, string(body)) 238 return nil, err 239 } 240 return timeseries, nil 241 } 242 243 func getSchema(fm kubeFramework, svc *kube_v1.Service) (*api_v1.TimeseriesSchema, error) { 244 body, err := fm.Client().CoreV1().RESTClient().Get(). 245 Namespace(svc.Namespace). 246 Prefix("proxy"). 247 Resource("services"). 248 Name(svc.Name). 249 Suffix(metricsSchemaEndpoint). 250 Do().Raw() 251 if err != nil { 252 return nil, err 253 } 254 var timeseriesSchema api_v1.TimeseriesSchema 255 if err := json.Unmarshal(body, ×eriesSchema); err != nil { 256 glog.V(2).Infof("Metrics schema error: %v %v", err, string(body)) 257 return nil, err 258 } 259 return ×eriesSchema, nil 260 } 261 262 var expectedSystemContainers = map[string]struct{}{ 263 "machine": {}, 264 "kubelet": {}, 265 "kube-proxy": {}, 266 // TODO(piosz): Uncomment once https://github.com/kubernetes/kubernetes/issues/37453 is fixed 267 // "system": {}, 268 "docker-daemon": {}, 269 } 270 271 func isContainerBaseImageExpected(ts *api_v1.Timeseries) bool { 272 cName := ts.Labels[core.LabelContainerName.Key] 273 // TODO(piosz): remove this if once https://github.com/kubernetes/kubernetes/issues/37453 is fixed 274 if cName == "system" { 275 return false 276 } 277 _, exists := expectedSystemContainers[cName] 278 return !exists 279 } 280 281 func runMetricExportTest(fm kubeFramework, svc *kube_v1.Service) error { 282 podList, err := fm.GetPodsRunningOnNodes() 283 if err != nil { 284 return err 285 } 286 expectedPods := make([]string, 0, len(podList)) 287 for _, pod := range podList { 288 expectedPods = append(expectedPods, pod.Name) 289 } 290 glog.V(0).Infof("Expected pods: %v", expectedPods) 291 292 expectedNodes, err := fm.GetNodeNames() 293 if err != nil { 294 return err 295 } 296 glog.V(0).Infof("Expected nodes: %v", expectedNodes) 297 298 timeseries, err := getTimeseries(fm, svc) 299 if err != nil { 300 return err 301 } 302 if len(timeseries) == 0 { 303 return fmt.Errorf("expected non zero timeseries") 304 } 305 schema, err := getSchema(fm, svc) 306 if err != nil { 307 return err 308 } 309 // Build a map of metric names to metric descriptors. 310 mdMap := map[string]*api_v1.MetricDescriptor{} 311 for idx := range schema.Metrics { 312 mdMap[schema.Metrics[idx].Name] = &schema.Metrics[idx] 313 } 314 actualPods := map[string]bool{} 315 actualNodes := map[string]bool{} 316 actualSystemContainers := map[string]map[string]struct{}{} 317 for _, ts := range timeseries { 318 // Verify the relevant labels are present. 319 // All common labels must be present. 320 podName, podMetric := ts.Labels[core.LabelPodName.Key] 321 322 for _, label := range core.CommonLabels() { 323 _, exists := ts.Labels[label.Key] 324 if !exists { 325 return fmt.Errorf("timeseries: %v does not contain common label: %v", ts, label) 326 } 327 } 328 if podMetric { 329 for _, label := range core.PodLabels() { 330 _, exists := ts.Labels[label.Key] 331 if !exists { 332 return fmt.Errorf("timeseries: %v does not contain pod label: %v", ts, label) 333 } 334 } 335 } 336 337 if podMetric { 338 actualPods[podName] = true 339 // Extra explicit check that the expecte metrics are there: 340 requiredLabels := []string{ 341 core.LabelPodNamespaceUID.Key, 342 core.LabelPodId.Key, 343 core.LabelHostID.Key, 344 // container name is checked later 345 } 346 for _, label := range requiredLabels { 347 _, exists := ts.Labels[label] 348 if !exists { 349 return fmt.Errorf("timeseries: %v does not contain required label: %v", ts, label) 350 } 351 } 352 353 } else { 354 if cName, ok := ts.Labels[core.LabelContainerName.Key]; ok { 355 hostname, ok := ts.Labels[core.LabelHostname.Key] 356 if !ok { 357 return fmt.Errorf("hostname label missing on container %+v", ts) 358 } 359 360 if cName == "machine" { 361 actualNodes[hostname] = true 362 } else { 363 for _, label := range core.ContainerLabels() { 364 if label == core.LabelContainerBaseImage && !isContainerBaseImageExpected(ts) { 365 continue 366 } 367 _, exists := ts.Labels[label.Key] 368 if !exists { 369 return fmt.Errorf("timeseries: %v does not contain container label: %v", ts, label) 370 } 371 } 372 } 373 374 if _, exists := expectedSystemContainers[cName]; exists { 375 if actualSystemContainers[cName] == nil { 376 actualSystemContainers[cName] = map[string]struct{}{} 377 } 378 actualSystemContainers[cName][hostname] = struct{}{} 379 } 380 } else { 381 return fmt.Errorf("container_name label missing on timeseries - %v", ts) 382 } 383 } 384 385 // Explicitly check for resource id 386 explicitRequirement := map[string][]string{ 387 core.MetricFilesystemUsage.MetricDescriptor.Name: {core.LabelResourceID.Key}, 388 core.MetricFilesystemLimit.MetricDescriptor.Name: {core.LabelResourceID.Key}, 389 core.MetricFilesystemAvailable.Name: {core.LabelResourceID.Key}, 390 core.MetricFilesystemInodes.Name: {core.LabelResourceID.Key}, 391 core.MetricFilesystemInodesFree.Name: {core.LabelResourceID.Key}, 392 core.MetricAcceleratorMemoryTotal.Name: {core.LabelAcceleratorMake.Key, core.LabelAcceleratorModel.Key, core.LabelAcceleratorID.Key}, 393 core.MetricAcceleratorMemoryUsed.Name: {core.LabelAcceleratorMake.Key, core.LabelAcceleratorModel.Key, core.LabelAcceleratorID.Key}, 394 core.MetricAcceleratorDutyCycle.Name: {core.LabelAcceleratorMake.Key, core.LabelAcceleratorModel.Key, core.LabelAcceleratorID.Key}, 395 core.MetricDiskIORead.Name: {core.LabelResourceID.Key}, 396 core.MetricDiskIOReadRate.Name: {core.LabelResourceID.Key}, 397 core.MetricDiskIOWrite.Name: {core.LabelResourceID.Key}, 398 core.MetricDiskIOWriteRate.Name: {core.LabelResourceID.Key}} 399 400 for metricName, points := range ts.Metrics { 401 md, exists := mdMap[metricName] 402 if !exists { 403 return fmt.Errorf("unexpected metric %q", metricName) 404 } 405 406 for _, point := range points { 407 for _, label := range md.Labels { 408 _, exists := point.Labels[label.Key] 409 if !exists { 410 return fmt.Errorf("metric %q point %v does not contain metric label: %v", metricName, point, label) 411 } 412 } 413 } 414 415 required := explicitRequirement[metricName] 416 for _, label := range required { 417 for _, point := range points { 418 _, exists := point.Labels[label] 419 if !exists { 420 return fmt.Errorf("metric %q point %v does not contain metric label: %v", metricName, point, label) 421 } 422 } 423 } 424 } 425 } 426 // Validate that system containers are running on all the nodes. 427 // This test could fail if one of the containers was down while the metrics sample was collected. 428 for cName, hosts := range actualSystemContainers { 429 for _, host := range expectedNodes { 430 if _, ok := hosts[host]; !ok { 431 return fmt.Errorf("System container %q not found on host: %q - %v", cName, host, actualSystemContainers) 432 } 433 } 434 } 435 436 if err := expectedItemsExist(expectedPods, actualPods); err != nil { 437 return fmt.Errorf("expected pods don't exist %v.\nExpected: %v\nActual:%v", err, expectedPods, actualPods) 438 } 439 if err := expectedItemsExist(expectedNodes, actualNodes); err != nil { 440 return fmt.Errorf("expected nodes don't exist %v.\nExpected: %v\nActual:%v", err, expectedNodes, actualNodes) 441 } 442 443 return nil 444 } 445 446 func expectedItemsExist(expectedItems []string, actualItems map[string]bool) error { 447 for _, item := range expectedItems { 448 if _, found := actualItems[item]; !found { 449 return fmt.Errorf("missing %s", item) 450 } 451 } 452 return nil 453 } 454 455 func getErrorCauses(err error) string { 456 serr, ok := err.(*apiErrors.StatusError) 457 if !ok { 458 return "" 459 } 460 var causes []string 461 for _, c := range serr.ErrStatus.Details.Causes { 462 causes = append(causes, c.Message) 463 } 464 return strings.Join(causes, ", ") 465 } 466 467 var labelSelectorEverything = labels.Everything() 468 469 func getDataFromProxy(fm kubeFramework, svc *kube_v1.Service, url string) ([]byte, error) { 470 glog.V(2).Infof("Querying heapster: %s", url) 471 return fm.Client().CoreV1().RESTClient().Get(). 472 Namespace(svc.Namespace). 473 Prefix("proxy"). 474 Resource("services"). 475 Name(svc.Name). 476 Suffix(url). 477 Do().Raw() 478 } 479 480 func getDataFromProxyWithSelector(fm kubeFramework, svc *kube_v1.Service, url string, labelSelector *labels.Selector) ([]byte, error) { 481 glog.V(2).Infof("Querying heapster: %s", url) 482 return fm.Client().CoreV1().RESTClient().Get(). 483 Namespace(svc.Namespace). 484 Prefix("proxy"). 485 Resource("services"). 486 Name(svc.Name). 487 Suffix(url).VersionedParams(&metav1.ListOptions{LabelSelector: (*labelSelector).String()}, metav1.ParameterCodec). 488 Do().Raw() 489 } 490 491 func getMetricResultList(fm kubeFramework, svc *kube_v1.Service, url string) (*api_v1.MetricResultList, error) { 492 body, err := getDataFromProxy(fm, svc, url) 493 if err != nil { 494 return nil, err 495 } 496 var data api_v1.MetricResultList 497 if err := json.Unmarshal(body, &data); err != nil { 498 glog.V(2).Infof("response body: %v", string(body)) 499 return nil, err 500 } 501 if err := checkMetricResultListSanity(&data); err != nil { 502 return nil, err 503 } 504 return &data, nil 505 } 506 507 func getMetricResult(fm kubeFramework, svc *kube_v1.Service, url string) (*api_v1.MetricResult, error) { 508 body, err := getDataFromProxy(fm, svc, url) 509 if err != nil { 510 return nil, err 511 } 512 var data api_v1.MetricResult 513 if err := json.Unmarshal(body, &data); err != nil { 514 glog.V(2).Infof("response body: %v", string(body)) 515 return nil, err 516 } 517 if err := checkMetricResultSanity(&data); err != nil { 518 return nil, err 519 } 520 return &data, nil 521 } 522 523 func getStringResult(fm kubeFramework, svc *kube_v1.Service, url string) ([]string, error) { 524 body, err := getDataFromProxy(fm, svc, url) 525 if err != nil { 526 return nil, err 527 } 528 var data []string 529 if err := json.Unmarshal(body, &data); err != nil { 530 glog.V(2).Infof("response body: %v", string(body)) 531 return nil, err 532 } 533 if len(data) == 0 { 534 return nil, fmt.Errorf("empty string array") 535 } 536 return data, nil 537 } 538 539 func checkMetricResultSanity(metrics *api_v1.MetricResult) error { 540 bytes, err := json.Marshal(*metrics) 541 if err != nil { 542 return err 543 } 544 stringVersion := string(bytes) 545 546 if len(metrics.Metrics) == 0 { 547 return fmt.Errorf("empty metrics: %s", stringVersion) 548 } 549 // There should be recent metrics in the response. 550 if time.Now().Sub(metrics.LatestTimestamp).Seconds() > 120 { 551 return fmt.Errorf("corrupted last timestamp: %s", stringVersion) 552 } 553 // Metrics don't have to be sorted, so the oldest one can be first. 554 if time.Now().Sub(metrics.Metrics[0].Timestamp).Hours() > 1 { 555 return fmt.Errorf("corrupted timestamp: %s", stringVersion) 556 } 557 if metrics.Metrics[0].Value > 10000 { 558 return fmt.Errorf("value too big: %s", stringVersion) 559 } 560 return nil 561 } 562 563 func checkMetricResultListSanity(metrics *api_v1.MetricResultList) error { 564 if len(metrics.Items) == 0 { 565 return fmt.Errorf("empty metrics") 566 } 567 for _, item := range metrics.Items { 568 err := checkMetricResultSanity(&item) 569 if err != nil { 570 return err 571 } 572 } 573 return nil 574 } 575 576 func runModelTest(fm kubeFramework, svc *kube_v1.Service) error { 577 podList, err := fm.GetPodsRunningOnNodes() 578 if err != nil { 579 return err 580 } 581 if len(podList) == 0 { 582 return fmt.Errorf("empty pod list") 583 } 584 nodeList, err := fm.GetNodeNames() 585 if err != nil { 586 return err 587 } 588 if len(nodeList) == 0 { 589 return fmt.Errorf("empty node list") 590 } 591 podNamesList := make([]string, 0, len(podList)) 592 for _, pod := range podList { 593 podNamesList = append(podNamesList, fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)) 594 } 595 596 glog.V(0).Infof("Expected pods: %v", podNamesList) 597 glog.V(0).Infof("Expected nodes: %v", nodeList) 598 allkeys, err := getStringResult(fm, svc, "/api/v1/model/debug/allkeys") 599 if err != nil { 600 return fmt.Errorf("Failed to get debug information about keys: %v", err) 601 } 602 glog.V(0).Infof("Available Heapster metric sets: %v", allkeys) 603 604 metricUrlsToCheck := []string{} 605 batchMetricsUrlsToCheck := []string{} 606 stringUrlsToCheck := []string{} 607 608 /* TODO: enable once cluster aggregator is added. 609 metricUrlsToCheck = append(metricUrlsToCheck, 610 fmt.Sprintf("/api/v1/model/metrics/%s", "cpu-usage"), 611 ) 612 */ 613 614 /* TODO: add once Cluster metrics aggregator is added. 615 "/api/v1/model/metrics", 616 "/api/v1/model/" 617 */ 618 stringUrlsToCheck = append(stringUrlsToCheck) 619 620 for _, node := range nodeList { 621 metricUrlsToCheck = append(metricUrlsToCheck, 622 fmt.Sprintf("/api/v1/model/nodes/%s/metrics/%s", node, "cpu/usage_rate"), 623 fmt.Sprintf("/api/v1/model/nodes/%s/metrics/%s", node, "cpu-usage"), 624 ) 625 626 stringUrlsToCheck = append(stringUrlsToCheck, 627 fmt.Sprintf("/api/v1/model/nodes/%s/metrics", node), 628 ) 629 } 630 631 for _, pod := range podList { 632 containerName := pod.Spec.Containers[0].Name 633 634 metricUrlsToCheck = append(metricUrlsToCheck, 635 fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics/%s", pod.Namespace, pod.Name, "cpu/usage_rate"), 636 fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics/%s", pod.Namespace, pod.Name, "cpu-usage"), 637 fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics/%s", pod.Namespace, pod.Name, containerName, "cpu/usage_rate"), 638 fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics/%s", pod.Namespace, pod.Name, containerName, "cpu-usage"), 639 fmt.Sprintf("/api/v1/model/namespaces/%s/metrics/%s", pod.Namespace, "cpu/usage_rate"), 640 fmt.Sprintf("/api/v1/model/namespaces/%s/metrics/%s", pod.Namespace, "cpu-usage"), 641 ) 642 643 batchMetricsUrlsToCheck = append(batchMetricsUrlsToCheck, 644 fmt.Sprintf("/api/v1/model/namespaces/%s/pod-list/%s,%s/metrics/%s", pod.Namespace, pod.Name, pod.Name, "cpu/usage_rate"), 645 fmt.Sprintf("/api/v1/model/namespaces/%s/pod-list/%s,%s/metrics/%s", pod.Namespace, pod.Name, pod.Name, "cpu-usage"), 646 ) 647 648 stringUrlsToCheck = append(stringUrlsToCheck, 649 fmt.Sprintf("/api/v1/model/namespaces/%s/metrics", pod.Namespace), 650 fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics", pod.Namespace, pod.Name), 651 fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics", pod.Namespace, pod.Name, containerName), 652 ) 653 } 654 655 for _, url := range metricUrlsToCheck { 656 _, err := getMetricResult(fm, svc, url) 657 if err != nil { 658 return fmt.Errorf("error while querying %s: %v", url, err) 659 } 660 } 661 662 for _, url := range batchMetricsUrlsToCheck { 663 _, err := getMetricResultList(fm, svc, url) 664 if err != nil { 665 return fmt.Errorf("error while querying %s: %v", url, err) 666 } 667 } 668 669 for _, url := range stringUrlsToCheck { 670 _, err := getStringResult(fm, svc, url) 671 if err != nil { 672 return fmt.Errorf("error while querying %s: %v", url, err) 673 } 674 } 675 return nil 676 } 677 678 const ( 679 apiPrefix = "apis" 680 metricsApiGroupName = "metrics.k8s.io" 681 metricsApiVersion = "v1alpha1" 682 ) 683 684 var baseMetricsUrl = fmt.Sprintf("%s/%s/%s", apiPrefix, metricsApiGroupName, metricsApiVersion) 685 686 func checkUsage(res kube_v1.ResourceList) error { 687 if _, found := res[kube_v1.ResourceCPU]; !found { 688 return fmt.Errorf("Cpu not found") 689 } 690 if _, found := res[kube_v1.ResourceMemory]; !found { 691 return fmt.Errorf("Memory not found") 692 } 693 return nil 694 } 695 696 func getPodMetrics(fm kubeFramework, svc *kube_v1.Service, pod kube_v1.Pod) (*metrics_api.PodMetrics, error) { 697 url := fmt.Sprintf("%s/namespaces/%s/pods/%s", baseMetricsUrl, pod.Namespace, pod.Name) 698 body, err := getDataFromProxy(fm, svc, url) 699 if err != nil { 700 return nil, err 701 } 702 var data metrics_api.PodMetrics 703 if err := json.Unmarshal(body, &data); err != nil { 704 glog.V(2).Infof("response body: %v", string(body)) 705 return nil, err 706 } 707 return &data, nil 708 } 709 710 func getAllPodsInNamespaceMetrics(fm kubeFramework, svc *kube_v1.Service, namespace string) (metrics_api.PodMetricsList, error) { 711 url := fmt.Sprintf("%s/namespaces/%s/pods/", baseMetricsUrl, namespace) 712 return getPodMetricsList(fm, svc, url, &labelSelectorEverything) 713 } 714 715 func getAllPodsMetrics(fm kubeFramework, svc *kube_v1.Service) (metrics_api.PodMetricsList, error) { 716 url := fmt.Sprintf("%s/pods/", baseMetricsUrl) 717 selector := labels.Everything() 718 return getPodMetricsList(fm, svc, url, &selector) 719 } 720 721 func getLabelSelectedPodMetrics(fm kubeFramework, svc *kube_v1.Service, namespace string, labelSelector *labels.Selector) (metrics_api.PodMetricsList, error) { 722 url := fmt.Sprintf("%s/namespaces/%s/pods/", baseMetricsUrl, namespace) 723 return getPodMetricsList(fm, svc, url, labelSelector) 724 } 725 726 func getPodMetricsList(fm kubeFramework, svc *kube_v1.Service, url string, labelSelector *labels.Selector) (metrics_api.PodMetricsList, error) { 727 body, err := getDataFromProxyWithSelector(fm, svc, url, labelSelector) 728 if err != nil { 729 return metrics_api.PodMetricsList{}, err 730 } 731 var data metrics_api.PodMetricsList 732 if err := json.Unmarshal(body, &data); err != nil { 733 glog.V(2).Infof("response body: %v", string(body)) 734 return metrics_api.PodMetricsList{}, err 735 } 736 return data, nil 737 } 738 739 func checkSinglePodMetrics(metrics *metrics_api.PodMetrics, pod *kube_v1.Pod) error { 740 if metrics.Name != pod.Name { 741 return fmt.Errorf("Wrong pod name: expected %v, got %v", pod.Name, metrics.Name) 742 } 743 if metrics.Namespace != pod.Namespace { 744 return fmt.Errorf("Wrong pod namespace: expected %v, got %v", pod.Namespace, metrics.Namespace) 745 } 746 if len(pod.Spec.Containers) != len(metrics.Containers) { 747 return fmt.Errorf("Wrong number of containers in returned metrics: expected %v, got %v", len(pod.Spec.Containers), len(metrics.Containers)) 748 } 749 for _, c := range metrics.Containers { 750 if err := checkUsage(c.Usage); err != nil { 751 return err 752 } 753 } 754 return nil 755 } 756 757 func getSingleNodeMetrics(fm kubeFramework, svc *kube_v1.Service, node string) (*metrics_api.NodeMetrics, error) { 758 url := fmt.Sprintf("%s/nodes/%s", baseMetricsUrl, node) 759 body, err := getDataFromProxy(fm, svc, url) 760 if err != nil { 761 return nil, err 762 } 763 var data metrics_api.NodeMetrics 764 if err := json.Unmarshal(body, &data); err != nil { 765 glog.V(2).Infof("response body: %v", string(body)) 766 return nil, err 767 } 768 return &data, nil 769 } 770 771 func getNodeMetricsList(fm kubeFramework, svc *kube_v1.Service, url string, labelSelector *labels.Selector) (metrics_api.NodeMetricsList, error) { 772 body, err := getDataFromProxyWithSelector(fm, svc, url, labelSelector) 773 if err != nil { 774 return metrics_api.NodeMetricsList{}, err 775 } 776 var data metrics_api.NodeMetricsList 777 if err := json.Unmarshal(body, &data); err != nil { 778 glog.V(2).Infof("response body: %v", string(body)) 779 return metrics_api.NodeMetricsList{}, err 780 } 781 return data, nil 782 } 783 784 func getLabelSelectedNodeMetrics(fm kubeFramework, svc *kube_v1.Service, labelSelector *labels.Selector) (metrics_api.NodeMetricsList, error) { 785 url := fmt.Sprintf("%s/nodes", baseMetricsUrl) 786 return getNodeMetricsList(fm, svc, url, labelSelector) 787 } 788 789 func getAllNodeMetrics(fm kubeFramework, svc *kube_v1.Service) (metrics_api.NodeMetricsList, error) { 790 url := fmt.Sprintf("%s/nodes", baseMetricsUrl) 791 selector := labels.Everything() 792 return getNodeMetricsList(fm, svc, url, &selector) 793 } 794 795 func runSingleNodeMetricsApiTest(fm kubeFramework, svc *kube_v1.Service) error { 796 nodeList, err := fm.GetNodeNames() 797 if err != nil { 798 return err 799 } 800 if len(nodeList) == 0 { 801 return fmt.Errorf("empty node list") 802 } 803 804 for _, node := range nodeList { 805 metrics, err := getSingleNodeMetrics(fm, svc, node) 806 if err != nil { 807 return err 808 } 809 if metrics.Name != node { 810 return fmt.Errorf("Wrong node name: expected %v, got %v", node, metrics.Name) 811 } 812 if err := checkUsage(metrics.Usage); err != nil { 813 return err 814 } 815 } 816 return nil 817 } 818 819 func runLabelSelectorNodeMetricsApiTest(fm kubeFramework, svc *kube_v1.Service) error { 820 nodeList, err := fm.GetNodes() 821 if err != nil { 822 return err 823 } 824 if len(nodeList.Items) == 0 { 825 return fmt.Errorf("empty node list") 826 } 827 labelMap := make(map[string]map[string]kube_v1.Node) 828 for _, n := range nodeList.Items { 829 for label, value := range n.Labels { 830 selector := label + "=" + value 831 if _, found := labelMap[selector]; !found { 832 labelMap[selector] = make(map[string]kube_v1.Node) 833 } 834 labelMap[selector][n.Name] = n 835 } 836 } 837 838 for selector, nodesWithLabel := range labelMap { 839 sel, err := labels.Parse(selector) 840 if err != nil { 841 return err 842 } 843 metrics, err := getLabelSelectedNodeMetrics(fm, svc, &sel) 844 if err != nil { 845 return err 846 } 847 if len(metrics.Items) != len(nodesWithLabel) { 848 return fmt.Errorf("Wrong number of label selected node metrics: expected %v, got %v", len(nodesWithLabel), len(metrics.Items)) 849 } 850 for _, nodeMetric := range metrics.Items { 851 node := nodesWithLabel[nodeMetric.Name] 852 if nodeMetric.Name != node.Name { 853 return fmt.Errorf("Wrong node name: expected %v, got %v", node.Name, nodeMetric.Name) 854 } 855 if err := checkUsage(nodeMetric.Usage); err != nil { 856 return err 857 } 858 } 859 } 860 return nil 861 } 862 863 func runAllNodesMetricsApiTest(fm kubeFramework, svc *kube_v1.Service) error { 864 nodeList, err := fm.GetNodeNames() 865 if err != nil { 866 return err 867 } 868 if len(nodeList) == 0 { 869 return fmt.Errorf("empty node list") 870 } 871 872 nodeNames := sets.NewString(nodeList...) 873 metrics, err := getAllNodeMetrics(fm, svc) 874 if err != nil { 875 return err 876 } 877 878 if len(metrics.Items) != len(nodeList) { 879 return fmt.Errorf("Wrong number of all node metrics: expected %v, got %v", len(nodeList), len(metrics.Items)) 880 } 881 for _, nodeMetrics := range metrics.Items { 882 if !nodeNames.Has(nodeMetrics.Name) { 883 return fmt.Errorf("Unexpected node name: %v, expected one of: %v", nodeMetrics.Name, nodeList) 884 } 885 if err := checkUsage(nodeMetrics.Usage); err != nil { 886 return err 887 } 888 } 889 return nil 890 } 891 892 func runSinglePodMetricsApiTest(fm kubeFramework, svc *kube_v1.Service) error { 893 podList, err := fm.GetAllRunningPods() 894 if err != nil { 895 return err 896 } 897 if len(podList) == 0 { 898 return fmt.Errorf("empty pod list") 899 } 900 for _, pod := range podList { 901 metrics, err := getPodMetrics(fm, svc, pod) 902 if err != nil { 903 return err 904 } 905 err = checkSinglePodMetrics(metrics, &pod) 906 if err != nil { 907 return err 908 } 909 } 910 return nil 911 } 912 913 func runAllPodsInNamespaceMetricsApiTest(fm kubeFramework, svc *kube_v1.Service) error { 914 podList, err := fm.GetAllRunningPods() 915 if err != nil { 916 return err 917 } 918 if len(podList) == 0 { 919 return fmt.Errorf("empty pod list") 920 } 921 nsToPods := make(map[string]map[string]kube_v1.Pod) 922 for _, pod := range podList { 923 if _, found := nsToPods[pod.Namespace]; !found { 924 nsToPods[pod.Namespace] = make(map[string]kube_v1.Pod) 925 } 926 nsToPods[pod.Namespace][pod.Name] = pod 927 } 928 929 for ns, podMap := range nsToPods { 930 metrics, err := getAllPodsInNamespaceMetrics(fm, svc, ns) 931 if err != nil { 932 return err 933 } 934 935 if len(metrics.Items) != len(nsToPods[ns]) { 936 return fmt.Errorf("Wrong number of metrics of all pods in a namespace: expected %v, got %v", len(nsToPods[ns]), len(metrics.Items)) 937 } 938 for _, podMetric := range metrics.Items { 939 pod := podMap[podMetric.Name] 940 err := checkSinglePodMetrics(&podMetric, &pod) 941 if err != nil { 942 return err 943 } 944 } 945 } 946 return nil 947 } 948 949 func runAllPodsMetricsApiTest(fm kubeFramework, svc *kube_v1.Service) error { 950 podList, err := fm.GetAllRunningPods() 951 if err != nil { 952 return err 953 } 954 if len(podList) == 0 { 955 return fmt.Errorf("empty pod list") 956 } 957 pods := make(map[string]kube_v1.Pod) 958 for _, p := range podList { 959 pods[p.Namespace+"/"+p.Name] = p 960 } 961 962 metrics, err := getAllPodsMetrics(fm, svc) 963 if err != nil { 964 return err 965 } 966 967 if len(metrics.Items) != len(podList) { 968 return fmt.Errorf("Wrong number of all pod metrics: expected %v, got %v", len(podList), len(metrics.Items)) 969 } 970 for _, podMetric := range metrics.Items { 971 pod := pods[podMetric.Namespace+"/"+podMetric.Name] 972 err := checkSinglePodMetrics(&podMetric, &pod) 973 if err != nil { 974 return err 975 } 976 } 977 return nil 978 } 979 980 func runLabelSelectorPodMetricsApiTest(fm kubeFramework, svc *kube_v1.Service) error { 981 podList, err := fm.GetAllRunningPods() 982 if err != nil { 983 return err 984 } 985 if len(podList) == 0 { 986 return fmt.Errorf("empty pod list") 987 } 988 nsToPods := make(map[string][]kube_v1.Pod) 989 for _, pod := range podList { 990 nsToPods[pod.Namespace] = append(nsToPods[pod.Namespace], pod) 991 } 992 993 for ns, podsInNamespace := range nsToPods { 994 labelMap := make(map[string]map[string]kube_v1.Pod) 995 for _, p := range podsInNamespace { 996 for label, name := range p.Labels { 997 selector := label + "=" + name 998 if _, found := labelMap[selector]; !found { 999 labelMap[selector] = make(map[string]kube_v1.Pod) 1000 } 1001 labelMap[selector][p.Name] = p 1002 } 1003 } 1004 for selector, podsWithLabel := range labelMap { 1005 sel, err := labels.Parse(selector) 1006 if err != nil { 1007 return err 1008 } 1009 metrics, err := getLabelSelectedPodMetrics(fm, svc, ns, &sel) 1010 if err != nil { 1011 return err 1012 } 1013 if len(metrics.Items) != len(podsWithLabel) { 1014 return fmt.Errorf("Wrong number of label selected pod metrics: expected %v, got %v", len(podsWithLabel), len(metrics.Items)) 1015 } 1016 for _, podMetric := range metrics.Items { 1017 pod := podsWithLabel[podMetric.Name] 1018 err := checkSinglePodMetrics(&podMetric, &pod) 1019 if err != nil { 1020 return err 1021 } 1022 } 1023 } 1024 } 1025 return nil 1026 } 1027 1028 func apiTest(kubeVersion string, zone string) error { 1029 fm, err := newKubeFramework(kubeVersion) 1030 if err != nil { 1031 return err 1032 } 1033 if err := buildAndPushDockerImages(fm, zone); err != nil { 1034 return err 1035 } 1036 1037 // Create heapster pod and service. 1038 svc, rc, err := getHeapsterRcAndSvc(fm) 1039 if err != nil { 1040 return err 1041 } 1042 ns := *namespace 1043 if err := deleteAll(fm, ns, svc, rc); err != nil { 1044 return err 1045 } 1046 if err := createAll(fm, ns, &svc, &rc); err != nil { 1047 return err 1048 } 1049 1050 setupHeapsterServiceAccount(fm) 1051 setupHeapsterRBAC(fm) 1052 1053 if err := fm.WaitUntilPodRunning(ns, rc.Spec.Template.Labels, time.Minute); err != nil { 1054 return err 1055 } 1056 if err := fm.WaitUntilServiceActive(svc, time.Minute); err != nil { 1057 return err 1058 } 1059 testFuncs := []func() error{ 1060 func() error { 1061 glog.V(2).Infof("Heapster metric export test...") 1062 err := runMetricExportTest(fm, svc) 1063 if err == nil { 1064 glog.V(2).Infof("Heapster metric export test: OK") 1065 } else { 1066 glog.V(2).Infof("Heapster metric export test: error: %v", err) 1067 } 1068 return err 1069 }, 1070 func() error { 1071 glog.V(2).Infof("Model test") 1072 err := runModelTest(fm, svc) 1073 if err == nil { 1074 glog.V(2).Infof("Model test: OK") 1075 } else { 1076 glog.V(2).Infof("Model test: error: %v", err) 1077 } 1078 return err 1079 }, 1080 func() error { 1081 glog.V(2).Infof("Metrics API test - single pod") 1082 err := runSinglePodMetricsApiTest(fm, svc) 1083 if err == nil { 1084 glog.V(2).Infof("Metrics API test - single pod: OK") 1085 } else { 1086 glog.V(2).Infof("Metrics API test - single pod: error: %v", err) 1087 } 1088 return err 1089 }, 1090 func() error { 1091 glog.V(2).Infof("Metrics API test - All pods in a namespace") 1092 err := runAllPodsInNamespaceMetricsApiTest(fm, svc) 1093 if err == nil { 1094 glog.V(2).Infof("Metrics API test - All pods in a namespace: OK") 1095 } else { 1096 glog.V(2).Infof("Metrics API test - All pods in a namespace: error: %v", err) 1097 } 1098 return err 1099 }, 1100 func() error { 1101 glog.V(2).Infof("Metrics API test - all pods") 1102 err := runAllPodsMetricsApiTest(fm, svc) 1103 if err == nil { 1104 glog.V(2).Infof("Metrics API test - all pods: OK") 1105 } else { 1106 glog.V(2).Infof("Metrics API test - all pods: error: %v", err) 1107 } 1108 return err 1109 }, 1110 func() error { 1111 glog.V(2).Infof("Metrics API test - label selector for pods") 1112 err := runLabelSelectorPodMetricsApiTest(fm, svc) 1113 if err == nil { 1114 glog.V(2).Infof("Metrics API test - label selector for pods: OK") 1115 } else { 1116 glog.V(2).Infof("Metrics API test - label selector for pods: error: %v", err) 1117 } 1118 return err 1119 }, 1120 func() error { 1121 glog.V(2).Infof("Metrics API test - single node") 1122 err := runSingleNodeMetricsApiTest(fm, svc) 1123 if err == nil { 1124 glog.V(2).Infof("Metrics API test - single node: OK") 1125 } else { 1126 glog.V(2).Infof("Metrics API test - single node: error: %v", err) 1127 } 1128 return err 1129 }, 1130 func() error { 1131 glog.V(2).Infof("Metrics API test - label selector for nodes") 1132 err := runLabelSelectorNodeMetricsApiTest(fm, svc) 1133 if err == nil { 1134 glog.V(2).Infof("Metrics API test - label selector for nodes: OK") 1135 } else { 1136 glog.V(2).Infof("Metrics API test - label selector for nodes: error: %v", err) 1137 } 1138 return err 1139 }, 1140 func() error { 1141 glog.V(2).Infof("Metrics API test - all nodes") 1142 err := runAllNodesMetricsApiTest(fm, svc) 1143 if err == nil { 1144 glog.V(2).Infof("Metrics API test - all nodes: OK") 1145 } else { 1146 glog.V(2).Infof("Metrics API test - all nodes: error: %v", err) 1147 } 1148 return err 1149 }, 1150 } 1151 attempts := *maxRetries 1152 glog.Infof("Starting tests") 1153 for { 1154 var err error 1155 for _, testFunc := range testFuncs { 1156 if err = testFunc(); err != nil { 1157 break 1158 } 1159 } 1160 if *runForever { 1161 continue 1162 } 1163 if err == nil { 1164 glog.V(2).Infof("All tests passed.") 1165 break 1166 } 1167 if attempts == 0 { 1168 glog.V(2).Info("Too many attempts.") 1169 return err 1170 } 1171 glog.V(2).Infof("Some tests failed. Retrying.") 1172 attempts-- 1173 time.Sleep(time.Second * 10) 1174 } 1175 deleteAll(fm, ns, svc, rc) 1176 removeHeapsterImage(fm, zone) 1177 return nil 1178 } 1179 1180 func runApiTest() error { 1181 tempDir, err := ioutil.TempDir("", "deploy") 1182 if err != nil { 1183 return nil 1184 } 1185 defer os.RemoveAll(tempDir) 1186 if *kubeVersions == "" { 1187 return apiTest("", *testZone) 1188 } 1189 kubeVersionsList := strings.Split(*kubeVersions, ",") 1190 for _, kubeVersion := range kubeVersionsList { 1191 if err := apiTest(kubeVersion, *testZone); err != nil { 1192 return err 1193 } 1194 } 1195 return nil 1196 } 1197 1198 func TestHeapster(t *testing.T) { 1199 if testing.Short() { 1200 t.Skip("skipping heapster kubernetes integration test.") 1201 } 1202 require.NoError(t, runApiTest()) 1203 }