github.com/qsunny/k8s@v0.0.0-20220101153623-e6dca256d5bf/heapster-1.3.0/integration/framework.go (about) 1 // Copyright 2014 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 "flag" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "os/exec" 23 "path/filepath" 24 "strings" 25 "time" 26 27 "github.com/golang/glog" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/labels" 30 "k8s.io/apimachinery/pkg/runtime" 31 kclient "k8s.io/client-go/kubernetes" 32 "k8s.io/client-go/pkg/api" 33 "k8s.io/client-go/pkg/api/v1" 34 kclientcmd "k8s.io/client-go/tools/clientcmd" 35 kclientcmdapi "k8s.io/client-go/tools/clientcmd/api" 36 ) 37 38 type kubeFramework interface { 39 // Kube client 40 Client() *kclient.Clientset 41 42 // Parses and Returns a replication Controller object contained in 'filePath' 43 ParseRC(filePath string) (*v1.ReplicationController, error) 44 45 // Parses and Returns a service object contained in 'filePath' 46 ParseService(filePath string) (*v1.Service, error) 47 48 // Creates a kube service. 49 CreateService(ns string, service *v1.Service) (*v1.Service, error) 50 51 // Creates a namespace. 52 CreateNs(ns *v1.Namespace) (*v1.Namespace, error) 53 54 // Creates a kube replication controller. 55 CreateRC(ns string, rc *v1.ReplicationController) (*v1.ReplicationController, error) 56 57 // Deletes a namespace 58 DeleteNs(ns string) error 59 60 // Destroy cluster 61 DestroyCluster() 62 63 // Returns a url that provides access to a kubernetes service via the proxy on the apiserver. 64 // This url requires master auth. 65 GetProxyUrlForService(service *v1.Service) string 66 67 // Returns the node hostnames. 68 GetNodeNames() ([]string, error) 69 70 // Returns the nodes. 71 GetNodes() (*v1.NodeList, error) 72 73 // Returns pod names in the cluster. 74 // TODO: Remove, or mix with namespace 75 GetRunningPodNames() ([]string, error) 76 77 // Returns pods in the cluster running outside kubernetes-master. 78 GetPodsRunningOnNodes() ([]v1.Pod, error) 79 80 // Returns pods in the cluster. 81 GetAllRunningPods() ([]v1.Pod, error) 82 83 WaitUntilPodRunning(ns string, podLabels map[string]string, timeout time.Duration) error 84 WaitUntilServiceActive(svc *v1.Service, timeout time.Duration) error 85 } 86 87 type realKubeFramework struct { 88 // Kube client. 89 kubeClient *kclient.Clientset 90 91 // The version of the kube cluster 92 version string 93 94 // Master IP for this framework 95 masterIP string 96 97 // The base directory of current kubernetes release. 98 baseDir string 99 } 100 101 const imageUrlTemplate = "https://github.com/kubernetes/kubernetes/releases/download/v%s/kubernetes.tar.gz" 102 103 var ( 104 kubeConfig = flag.String("kube_config", os.Getenv("HOME")+"/.kube/config", "Path to cluster info file.") 105 workDir = flag.String("work_dir", "/tmp/heapster_test", "Filesystem path where test files will be stored. Files will persist across runs to speed up tests.") 106 ) 107 108 func exists(path string) bool { 109 if _, err := os.Stat(path); err != nil { 110 glog.V(2).Infof("%q does not exist", path) 111 return false 112 } 113 return true 114 } 115 116 const pathToGCEConfig = "cluster/gce/config-default.sh" 117 118 func disableClusterMonitoring(kubeBaseDir string) error { 119 kubeConfigFilePath := filepath.Join(kubeBaseDir, pathToGCEConfig) 120 input, err := ioutil.ReadFile(kubeConfigFilePath) 121 if err != nil { 122 return err 123 } 124 125 lines := strings.Split(string(input), "\n") 126 127 for i, line := range lines { 128 if strings.Contains(line, "ENABLE_CLUSTER_MONITORING") { 129 lines[i] = "ENABLE_CLUSTER_MONITORING=false" 130 } else if strings.Contains(line, "NUM_MINIONS=") { 131 lines[i] = "NUM_MINIONS=2" 132 } 133 } 134 output := strings.Join(lines, "\n") 135 return ioutil.WriteFile(kubeConfigFilePath, []byte(output), 0644) 136 } 137 138 func runKubeClusterCommand(kubeBaseDir, command string) ([]byte, error) { 139 cmd := exec.Command(filepath.Join(kubeBaseDir, "cluster", command)) 140 glog.V(2).Infof("about to run %v", cmd) 141 return cmd.CombinedOutput() 142 } 143 144 func setupNewCluster(kubeBaseDir string) error { 145 cmd := "kube-up.sh" 146 destroyCluster(kubeBaseDir) 147 out, err := runKubeClusterCommand(kubeBaseDir, cmd) 148 if err != nil { 149 glog.Errorf("failed to bring up cluster - %q\n%s", err, out) 150 return fmt.Errorf("failed to bring up cluster - %q", err) 151 } 152 glog.V(2).Info(string(out)) 153 glog.V(2).Infof("Giving the cluster 30 sec to stabilize") 154 time.Sleep(30 * time.Second) 155 return nil 156 } 157 158 func destroyCluster(kubeBaseDir string) error { 159 if kubeBaseDir == "" { 160 glog.Infof("Skipping cluster tear down since kubernetes repo base path is not set.") 161 return nil 162 } 163 glog.V(1).Info("Bringing down any existing kube cluster") 164 out, err := runKubeClusterCommand(kubeBaseDir, "kube-down.sh") 165 if err != nil { 166 glog.Errorf("failed to tear down cluster - %q\n%s", err, out) 167 return fmt.Errorf("failed to tear down kube cluster - %q", err) 168 } 169 170 return nil 171 } 172 173 func downloadRelease(workDir, version string) error { 174 // Temporary download path. 175 downloadPath := filepath.Join(workDir, "kube") 176 // Format url. 177 downloadUrl := fmt.Sprintf(imageUrlTemplate, version) 178 glog.V(1).Infof("About to download kube release using url: %q", downloadUrl) 179 180 // Download kube code and store it in a temp dir. 181 if err := exec.Command("wget", downloadUrl, "-O", downloadPath).Run(); err != nil { 182 return fmt.Errorf("failed to wget kubernetes release @ %q - %v", downloadUrl, err) 183 } 184 185 // Un-tar kube release. 186 if err := exec.Command("tar", "-xf", downloadPath, "-C", workDir).Run(); err != nil { 187 return fmt.Errorf("failed to un-tar kubernetes release at %q - %v", downloadPath, err) 188 } 189 return nil 190 } 191 192 func getKubeClient() (string, *kclient.Clientset, error) { 193 c, err := kclientcmd.LoadFromFile(*kubeConfig) 194 if err != nil { 195 return "", nil, fmt.Errorf("error loading kubeConfig: %v", err.Error()) 196 } 197 if c.CurrentContext == "" || len(c.Clusters) == 0 { 198 return "", nil, fmt.Errorf("invalid kubeConfig: %+v", *c) 199 } 200 config, err := kclientcmd.NewDefaultClientConfig( 201 *c, 202 &kclientcmd.ConfigOverrides{ 203 ClusterInfo: kclientcmdapi.Cluster{ 204 APIVersion: "v1", 205 }, 206 }).ClientConfig() 207 if err != nil { 208 return "", nil, fmt.Errorf("error parsing kubeConfig: %v", err.Error()) 209 } 210 kubeClient, err := kclient.NewForConfig(config) 211 if err != nil { 212 return "", nil, fmt.Errorf("error creating client - %q", err) 213 } 214 215 return c.Clusters[c.CurrentContext].Server, kubeClient, nil 216 } 217 218 func validateCluster(baseDir string) bool { 219 glog.V(1).Info("validating existing cluster") 220 out, err := runKubeClusterCommand(baseDir, "validate-cluster.sh") 221 if err != nil { 222 glog.V(1).Infof("cluster validation failed - %q\n %s", err, out) 223 return false 224 } 225 return true 226 } 227 228 func requireNewCluster(baseDir, version string) bool { 229 // Setup kube client 230 _, kubeClient, err := getKubeClient() 231 if err != nil { 232 glog.V(1).Infof("kube client creation failed - %q", err) 233 return true 234 } 235 glog.V(1).Infof("checking if existing cluster can be used") 236 versionInfo, err := kubeClient.ServerVersion() 237 if err != nil { 238 glog.V(1).Infof("failed to get kube version info - %q", err) 239 return true 240 } 241 return !strings.Contains(versionInfo.GitVersion, version) 242 } 243 244 func requireDownload(baseDir string) bool { 245 // Check that cluster scripts are present. 246 return !exists(filepath.Join(baseDir, "cluster", "kube-up.sh")) || 247 !exists(filepath.Join(baseDir, "cluster", "kube-down.sh")) || 248 !exists(filepath.Join(baseDir, "cluster", "validate-cluster.sh")) 249 } 250 251 func downloadAndSetupCluster(version string) (baseDir string, err error) { 252 // Create a temp dir to store the kube release files. 253 tempDir := filepath.Join(*workDir, version) 254 if !exists(tempDir) { 255 if err := os.MkdirAll(tempDir, 0700); err != nil { 256 return "", fmt.Errorf("failed to create a temp dir at %s - %q", tempDir, err) 257 } 258 glog.V(1).Infof("Successfully setup work dir at %s", tempDir) 259 } 260 261 kubeBaseDir := filepath.Join(tempDir, "kubernetes") 262 263 if requireDownload(kubeBaseDir) { 264 if exists(kubeBaseDir) { 265 os.RemoveAll(kubeBaseDir) 266 } 267 if err := downloadRelease(tempDir, version); err != nil { 268 return "", err 269 } 270 glog.V(1).Infof("Successfully downloaded kubernetes release at %s", tempDir) 271 } 272 273 // Disable monitoring 274 if err := disableClusterMonitoring(kubeBaseDir); err != nil { 275 return "", fmt.Errorf("failed to disable cluster monitoring in kube cluster config - %q", err) 276 } 277 glog.V(1).Info("Disabled cluster monitoring") 278 if !requireNewCluster(kubeBaseDir, version) { 279 glog.V(1).Infof("skipping cluster setup since a cluster with required version already exists") 280 return kubeBaseDir, nil 281 } 282 283 // Setup kube cluster 284 glog.V(1).Infof("Setting up new kubernetes cluster version: %s", version) 285 if err := os.Setenv("KUBERNETES_SKIP_CONFIRM", "y"); err != nil { 286 return "", err 287 } 288 if err := setupNewCluster(kubeBaseDir); err != nil { 289 // Cluster setup failed for some reason. 290 // Attempting to validate the cluster to see if it failed in the validate phase. 291 sleepDuration := 10 * time.Second 292 clusterReady := false 293 for i := 0; i < int(time.Minute/sleepDuration); i++ { 294 if !validateCluster(kubeBaseDir) { 295 glog.Infof("Retry validation after %v seconds.", sleepDuration/time.Second) 296 time.Sleep(sleepDuration) 297 } else { 298 clusterReady = true 299 break 300 } 301 } 302 if !clusterReady { 303 return "", fmt.Errorf("failed to setup cluster - %q", err) 304 } 305 } 306 glog.V(1).Infof("Successfully setup new kubernetes cluster version %s", version) 307 308 return kubeBaseDir, nil 309 } 310 311 func newKubeFramework(version string) (kubeFramework, error) { 312 var err error 313 kubeBaseDir := "" 314 if version != "" { 315 if len(strings.Split(version, ".")) != 3 { 316 return nil, fmt.Errorf("invalid kubernetes version specified - %q", version) 317 } 318 kubeBaseDir, err = downloadAndSetupCluster(version) 319 if err != nil { 320 return nil, err 321 } 322 } 323 324 // Setup kube client 325 masterIP, kubeClient, err := getKubeClient() 326 if err != nil { 327 return nil, err 328 } 329 return &realKubeFramework{ 330 kubeClient: kubeClient, 331 baseDir: kubeBaseDir, 332 version: version, 333 masterIP: masterIP, 334 }, nil 335 } 336 337 func (self *realKubeFramework) Client() *kclient.Clientset { 338 return self.kubeClient 339 } 340 341 func (self *realKubeFramework) loadObject(filePath string) (runtime.Object, error) { 342 data, err := ioutil.ReadFile(filePath) 343 if err != nil { 344 return nil, fmt.Errorf("failed to read object: %v", err) 345 } 346 obj, _, err := api.Codecs.UniversalDecoder(v1.SchemeGroupVersion).Decode(data, nil, nil) 347 return obj, err 348 } 349 350 func (self *realKubeFramework) ParseRC(filePath string) (*v1.ReplicationController, error) { 351 obj, err := self.loadObject(filePath) 352 if err != nil { 353 return nil, err 354 } 355 356 rc, ok := obj.(*v1.ReplicationController) 357 if !ok { 358 return nil, fmt.Errorf("Failed to cast replicationController: %#v", obj) 359 } 360 return rc, nil 361 } 362 363 func (self *realKubeFramework) ParseService(filePath string) (*v1.Service, error) { 364 obj, err := self.loadObject(filePath) 365 if err != nil { 366 return nil, err 367 } 368 service, ok := obj.(*v1.Service) 369 if !ok { 370 return nil, fmt.Errorf("Failed to cast service: %v", obj) 371 } 372 return service, nil 373 } 374 375 func (self *realKubeFramework) CreateService(ns string, service *v1.Service) (*v1.Service, error) { 376 service.Namespace = ns 377 newSvc, err := self.kubeClient.Services(ns).Create(service) 378 return newSvc, err 379 } 380 381 func (self *realKubeFramework) DeleteNs(ns string) error { 382 383 _, err := self.kubeClient.Namespaces().Get(ns, metav1.GetOptions{}) 384 if err != nil { 385 glog.V(0).Infof("Cannot get namespace %q. Skipping deletion: %s", ns, err) 386 return nil 387 } 388 glog.V(0).Infof("Deleting namespace %s", ns) 389 self.kubeClient.Namespaces().Delete(ns, nil) 390 391 for i := 0; i < 5; i++ { 392 glog.V(0).Infof("Checking for namespace %s", ns) 393 _, err := self.kubeClient.Namespaces().Get(ns, metav1.GetOptions{}) 394 if err != nil { 395 glog.V(0).Infof("%s doesn't exist", ns) 396 return nil 397 } 398 time.Sleep(10 * time.Second) 399 } 400 return fmt.Errorf("Namespace %s still exists", ns) 401 } 402 403 func (self *realKubeFramework) CreateNs(ns *v1.Namespace) (*v1.Namespace, error) { 404 return self.kubeClient.Namespaces().Create(ns) 405 } 406 407 func (self *realKubeFramework) CreateRC(ns string, rc *v1.ReplicationController) (*v1.ReplicationController, error) { 408 rc.Namespace = ns 409 return self.kubeClient.ReplicationControllers(ns).Create(rc) 410 } 411 412 func (self *realKubeFramework) DestroyCluster() { 413 destroyCluster(self.baseDir) 414 } 415 416 func (self *realKubeFramework) GetProxyUrlForService(service *v1.Service) string { 417 return fmt.Sprintf("%s/api/v1/proxy/namespaces/default/services/%s/", self.masterIP, service.Name) 418 } 419 420 func (self *realKubeFramework) GetNodeNames() ([]string, error) { 421 var nodes []string 422 nodeList, err := self.GetNodes() 423 if err != nil { 424 return nodes, err 425 } 426 for _, node := range nodeList.Items { 427 nodes = append(nodes, node.Name) 428 } 429 return nodes, nil 430 } 431 432 func (self *realKubeFramework) GetNodes() (*v1.NodeList, error) { 433 return self.kubeClient.Nodes().List(metav1.ListOptions{}) 434 } 435 436 func (self *realKubeFramework) GetAllRunningPods() ([]v1.Pod, error) { 437 return getRunningPods(true, self.kubeClient) 438 } 439 440 func (self *realKubeFramework) GetPodsRunningOnNodes() ([]v1.Pod, error) { 441 return getRunningPods(false, self.kubeClient) 442 } 443 444 func getRunningPods(includeMaster bool, kubeClient *kclient.Clientset) ([]v1.Pod, error) { 445 glog.V(0).Infof("Getting running pods") 446 podList, err := kubeClient.Pods(v1.NamespaceAll).List(metav1.ListOptions{}) 447 if err != nil { 448 return nil, err 449 } 450 pods := []v1.Pod{} 451 for _, pod := range podList.Items { 452 if pod.Status.Phase == v1.PodRunning { 453 if includeMaster || !isMasterNode(pod.Spec.NodeName) { 454 pods = append(pods, pod) 455 } 456 } 457 } 458 return pods, nil 459 } 460 461 func isMasterNode(nodeName string) bool { 462 return strings.Contains(nodeName, "kubernetes-master") 463 } 464 465 func (self *realKubeFramework) GetRunningPodNames() ([]string, error) { 466 var pods []string 467 podList, err := self.GetAllRunningPods() 468 if err != nil { 469 return pods, err 470 } 471 for _, pod := range podList { 472 pods = append(pods, string(pod.Name)) 473 } 474 return pods, nil 475 } 476 477 func (rkf *realKubeFramework) WaitUntilPodRunning(ns string, podLabels map[string]string, timeout time.Duration) error { 478 glog.V(2).Infof("Waiting for pod %v in %s...", podLabels, ns) 479 podsInterface := rkf.Client().Pods(ns) 480 for i := 0; i < int(timeout/time.Second); i++ { 481 podList, err := podsInterface.List(metav1.ListOptions{ 482 LabelSelector: labels.Set(podLabels).AsSelector().String(), 483 }) 484 if err != nil { 485 glog.V(1).Info(err) 486 return err 487 } 488 if len(podList.Items) > 0 { 489 podSpec := podList.Items[0] 490 if podSpec.Status.Phase == v1.PodRunning { 491 return nil 492 } 493 } 494 time.Sleep(time.Second) 495 } 496 return fmt.Errorf("pod not in running state after %d", timeout/time.Second) 497 } 498 499 func (rkf *realKubeFramework) WaitUntilServiceActive(svc *v1.Service, timeout time.Duration) error { 500 glog.V(2).Infof("Waiting for endpoints in service %s/%s", svc.Namespace, svc.Name) 501 for i := 0; i < int(timeout/time.Second); i++ { 502 e, err := rkf.Client().Endpoints(svc.Namespace).Get(svc.Name, metav1.GetOptions{}) 503 if err != nil { 504 return err 505 } 506 if len(e.Subsets) > 0 { 507 return nil 508 } 509 time.Sleep(time.Second) 510 } 511 512 return fmt.Errorf("Service %q not active after %d seconds - no endpoints found", svc.Name, timeout/time.Second) 513 514 }