...

Source file src/github.com/redhat-developer/odo/tests/helper/helper_kubectl.go

Documentation: github.com/redhat-developer/odo/tests/helper

     1  package helper
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	"github.com/onsi/gomega/gexec"
    14  
    15  	"github.com/redhat-developer/odo/pkg/labels"
    16  )
    17  
    18  const (
    19  	ResourceTypeDeployment = "deployment"
    20  	ResourceTypePod        = "pod"
    21  	ResourceTypeJob        = "job"
    22  	ResourceTypePVC        = "pvc"
    23  	ResourceTypeService    = "service"
    24  )
    25  
    26  type KubectlRunner struct {
    27  	// path to kubectl binary
    28  	path string
    29  }
    30  
    31  // NewKubectlRunner initializes new KubectlRunner
    32  func NewKubectlRunner(kubectlPath string) KubectlRunner {
    33  	return KubectlRunner{
    34  		path: kubectlPath,
    35  	}
    36  }
    37  
    38  // Run kubectl with given arguments
    39  func (kubectl KubectlRunner) Run(args ...string) *gexec.Session {
    40  	session := CmdRunner(kubectl.path, args...)
    41  	Eventually(session).Should(gexec.Exit(0))
    42  	return session
    43  }
    44  
    45  // Exec allows generic execution of commands, returning the contents of stdout
    46  func (kubectl KubectlRunner) Exec(podName string, projectName string, args []string, expectedSuccess *bool) (string, string) {
    47  
    48  	cmd := []string{"exec", podName, "--namespace", projectName}
    49  
    50  	cmd = append(cmd, args...)
    51  
    52  	cmdWrapper := Cmd(kubectl.path, cmd...)
    53  	if expectedSuccess == nil {
    54  		cmdWrapper = cmdWrapper.ShouldRun()
    55  	} else if *expectedSuccess {
    56  		cmdWrapper = cmdWrapper.ShouldPass()
    57  	} else {
    58  		cmdWrapper = cmdWrapper.ShouldFail()
    59  	}
    60  	return cmdWrapper.OutAndErr()
    61  }
    62  
    63  // ExecListDir returns dir list in specified location of pod
    64  func (kubectl KubectlRunner) ExecListDir(podName string, projectName string, dir string) string {
    65  	stdOut := Cmd(kubectl.path, "exec", podName, "--namespace", projectName,
    66  		"--", "ls", "-lai", dir).ShouldPass().Out()
    67  	return stdOut
    68  }
    69  
    70  // CheckCmdOpInRemoteDevfilePod runs the provided command on remote component pod and returns the return value of command output handler function passed to it
    71  func (kubectl KubectlRunner) CheckCmdOpInRemoteDevfilePod(podName string, containerName string, prjName string, cmd []string, checkOp func(cmdOp string, err error) bool) bool {
    72  	var execOptions []string
    73  	execOptions = []string{"exec", podName, "--namespace", prjName, "--"}
    74  	if containerName != "" {
    75  		execOptions = []string{"exec", podName, "-c", containerName, "--namespace", prjName, "--"}
    76  	}
    77  	args := append(execOptions, cmd...)
    78  	session := CmdRunner(kubectl.path, args...)
    79  	stdOut := string(session.Wait().Out.Contents())
    80  	stdErr := string(session.Wait().Err.Contents())
    81  	if stdErr != "" && session.ExitCode() != 0 {
    82  		return checkOp(stdOut, fmt.Errorf("cmd %s failed with error %s on pod %s", cmd, stdErr, podName))
    83  	}
    84  	return checkOp(stdOut, nil)
    85  }
    86  
    87  // GetRunningPodNameByComponent executes kubectl command and returns the running pod name of a deployed
    88  // devfile component by passing component name as a argument
    89  func (kubectl KubectlRunner) GetRunningPodNameByComponent(compName string, namespace string) string {
    90  	selector := fmt.Sprintf("--selector=component=%s", compName)
    91  	stdOut := Cmd(kubectl.path, "get", ResourceTypePod, "--namespace", namespace, "--field-selector=status.phase=Running", selector, "-o", "jsonpath={.items[*].metadata.name}").ShouldPass().Out()
    92  	return strings.TrimSpace(stdOut)
    93  }
    94  
    95  // GetJobNameByComponent executes kubectl command and returns the running job name
    96  func (kubectl KubectlRunner) GetJobNameByComponent(compName string, namespace string) string {
    97  	selector := fmt.Sprintf("--selector=app.kubernetes.io/instance=%s", compName)
    98  	stdOut := Cmd(kubectl.path, "get", ResourceTypeJob, "--namespace", namespace, selector, "-o", "jsonpath={.items[*].metadata.name}").ShouldPass().Out()
    99  	return strings.TrimSpace(stdOut)
   100  }
   101  
   102  // GetPVCSize executes kubectl command and returns the bound storage size
   103  func (kubectl KubectlRunner) GetPVCSize(compName, storageName, namespace string) string {
   104  	selector := fmt.Sprintf("--selector=app.kubernetes.io/storage-name=%s,app.kubernetes.io/instance=%s", storageName, compName)
   105  	stdOut := Cmd(kubectl.path, "get", ResourceTypePVC, "--namespace", namespace, selector, "-o", "jsonpath={.items[*].spec.resources.requests.storage}").ShouldPass().Out()
   106  	return strings.TrimSpace(stdOut)
   107  }
   108  
   109  // GetPodInitContainers executes kubectl command and returns the init containers of the pod
   110  func (kubectl KubectlRunner) GetPodInitContainers(compName string, namespace string) []string {
   111  	selector := fmt.Sprintf("--selector=component=%s", compName)
   112  	stdOut := Cmd(kubectl.path, "get", ResourceTypePod, "--namespace", namespace, "--field-selector=status.phase=Running", selector, "-o", "jsonpath={.items[*].spec.initContainers[*].name}").ShouldPass().Out()
   113  	return strings.Split(stdOut, " ")
   114  }
   115  
   116  // GetVolumeMountNamesandPathsFromContainer returns the volume name and mount path in the format name:path\n
   117  func (kubectl KubectlRunner) GetVolumeMountNamesandPathsFromContainer(deployName string, containerName, namespace string) string {
   118  	volumeName := Cmd(kubectl.path, "get", "deploy", deployName, "--namespace", namespace,
   119  		"-o", "go-template="+
   120  			"{{range .spec.template.spec.containers}}{{if eq .name \""+containerName+
   121  			"\"}}{{range .volumeMounts}}{{.name}}{{\":\"}}{{.mountPath}}{{\"\\n\"}}{{end}}{{end}}{{end}}").ShouldPass().Out()
   122  
   123  	return strings.TrimSpace(volumeName)
   124  }
   125  
   126  // GetContainerEnv returns the container env in the format name:value\n
   127  func (kubectl KubectlRunner) GetContainerEnv(podName, containerName, namespace string) string {
   128  	containerEnv := Cmd(kubectl.path, "get", "po", podName, "--namespace", namespace,
   129  		"-o", "go-template="+
   130  			"{{range .spec.containers}}{{if eq .name \""+containerName+
   131  			"\"}}{{range .env}}{{.name}}{{\":\"}}{{.value}}{{\"\\n\"}}{{end}}{{end}}{{end}}").ShouldPass().Out()
   132  
   133  	return strings.TrimSpace(containerEnv)
   134  }
   135  
   136  // WaitAndCheckForExistence wait for the given and checks if the given resource type gets deleted on the cluster
   137  func (kubectl KubectlRunner) WaitAndCheckForExistence(resourceType, namespace string, timeoutMinutes int) bool {
   138  	pingTimeout := time.After(time.Duration(timeoutMinutes) * time.Minute)
   139  	// this is a test package so time.Tick() is acceptable
   140  	// nolint
   141  	tick := time.Tick(time.Second)
   142  	for {
   143  		select {
   144  		case <-pingTimeout:
   145  			Fail(fmt.Sprintf("Timeout after %d minutes", timeoutMinutes))
   146  
   147  		case <-tick:
   148  			session := CmdRunner(kubectl.path, "get", resourceType, "--namespace", namespace)
   149  			Eventually(session).Should(gexec.Exit(0))
   150  			// https://github.com/kubernetes/kubectl/issues/847
   151  			output := string(session.Wait().Err.Contents())
   152  
   153  			if strings.Contains(strings.ToLower(output), "no resources found") {
   154  				return true
   155  			}
   156  		}
   157  	}
   158  }
   159  
   160  // GetServices gets services on the cluster
   161  func (kubectl KubectlRunner) GetServices(namespace string) string {
   162  	session := CmdRunner(kubectl.path, "get", ResourceTypeService, "--namespace", namespace)
   163  	Eventually(session).Should(gexec.Exit(0))
   164  	output := string(session.Wait().Out.Contents())
   165  	return output
   166  }
   167  
   168  // CreateAndSetRandNamespaceProject create and set new project
   169  func (kubectl KubectlRunner) CreateAndSetRandNamespaceProject() string {
   170  	projectName := GenerateProjectName()
   171  	kubectl.createAndSetRandNamespaceProject(projectName)
   172  	return projectName
   173  }
   174  
   175  func (kubectl KubectlRunner) createAndSetRandNamespaceProject(projectName string) string {
   176  	if kubectl.HasNamespaceProject(projectName) {
   177  		fmt.Fprintf(GinkgoWriter, "Namespace %q already exists\n", projectName)
   178  	} else {
   179  		fmt.Fprintf(GinkgoWriter, "Creating a new project: %s\n", projectName)
   180  		Cmd("kubectl", "create", "namespace", projectName).ShouldPass()
   181  	}
   182  	Cmd("kubectl", "config", "set-context", "--current", "--namespace", projectName).ShouldPass()
   183  	// ListNamespaceProject makes sure that project eventually appears in the list of all namespaces/projects.
   184  	kubectl.ListNamespaceProject(projectName)
   185  	kubectl.addConfigMapForCleanup(projectName) // add configmap for cleanup
   186  	return projectName
   187  }
   188  
   189  func (kubectl KubectlRunner) SetProject(namespace string) string {
   190  	Cmd("kubectl", "config", "set-context", "--current", "--namespace", namespace).ShouldPass()
   191  	session := Cmd("kubectl", "get", "namespaces").ShouldPass().Out()
   192  	Expect(session).To(ContainSubstring(namespace))
   193  	return namespace
   194  }
   195  
   196  // CreateRandNamespaceProjectOfLength create new project with i as the length of the name and sets it to the current context
   197  func (kubectl KubectlRunner) CreateAndSetRandNamespaceProjectOfLength(i int) string {
   198  	projectName := RandString(i)
   199  	kubectl.createAndSetRandNamespaceProject(projectName)
   200  	return projectName
   201  }
   202  
   203  // DeleteNamespaceProject deletes a specified project in kubernetes cluster
   204  func (kubectl KubectlRunner) DeleteNamespaceProject(projectName string, wait bool) {
   205  	fmt.Fprintf(GinkgoWriter, "Deleting project: %s\n", projectName)
   206  	Cmd("kubectl", "delete", "namespaces", projectName, "--wait="+strconv.FormatBool(wait)).ShouldPass()
   207  }
   208  
   209  func (kubectl KubectlRunner) GetEnvsDevFileDeployment(componentName, appName, projectName string) map[string]string {
   210  	var mapOutput = make(map[string]string)
   211  	selector := labels.Builder().WithComponentName(componentName).WithAppName(appName).SelectorFlag()
   212  	output := Cmd(kubectl.path, "get", ResourceTypeDeployment, selector, "--namespace", projectName,
   213  		"-o", "jsonpath='{range .items[0].spec.template.spec.containers[0].env[*]}{.name}:{.value}{\"\\n\"}{end}'").ShouldPass().Out()
   214  
   215  	for _, line := range strings.Split(output, "\n") {
   216  		line = strings.TrimPrefix(line, "'")
   217  		splits := strings.Split(line, ":")
   218  		name := splits[0]
   219  		value := strings.Join(splits[1:], ":")
   220  		mapOutput[name] = value
   221  	}
   222  	return mapOutput
   223  }
   224  
   225  func (kubectl KubectlRunner) GetAllPVCNames(namespace string) []string {
   226  	session := CmdRunner(kubectl.path, "get", ResourceTypePVC, "--namespace", namespace, "-o", "jsonpath={.items[*].metadata.name}")
   227  	Eventually(session).Should(gexec.Exit(0))
   228  	output := string(session.Wait().Out.Contents())
   229  	if output == "" {
   230  		return []string{}
   231  	}
   232  	return strings.Split(output, " ")
   233  }
   234  
   235  // DeletePod deletes a specified pod in the namespace
   236  func (kubectl KubectlRunner) DeletePod(podName string, namespace string) {
   237  	Cmd(kubectl.path, "delete", ResourceTypePod, "--namespace", namespace, podName).ShouldPass()
   238  }
   239  
   240  // WaitAndCheckForTerminatingState waits for the given interval
   241  // and checks if the given resource type has been deleted on the cluster or is in the terminating state
   242  func (kubectl KubectlRunner) WaitAndCheckForTerminatingState(resourceType, namespace string, timeoutMinutes int) bool {
   243  	return WaitAndCheckForTerminatingState(kubectl.path, resourceType, namespace, timeoutMinutes)
   244  }
   245  
   246  // VerifyResourceDeleted verifies if the given resource is deleted from cluster.
   247  func (kubectl KubectlRunner) VerifyResourceDeleted(ri ResourceInfo) {
   248  	session := CmdRunner(kubectl.path, "get", ri.ResourceType, "--namespace", ri.Namespace)
   249  	Eventually(session).Should(gexec.Exit(0))
   250  	output := string(session.Wait().Out.Contents())
   251  	Expect(output).NotTo(ContainSubstring(ri.ResourceName))
   252  }
   253  
   254  // VerifyResourceToBeDeleted verifies if a resource if deleted, or if not, if it is marked for deletion
   255  func (kubectl KubectlRunner) VerifyResourceToBeDeleted(ri ResourceInfo) {
   256  	deletedOrMarkedToDelete := func() bool {
   257  		session := CmdRunner(kubectl.path, "get", ri.ResourceType, ri.ResourceName, "--namespace", ri.Namespace, "-o", "jsonpath='{.metadata.deletionTimestamp}'")
   258  		exit := session.Wait().ExitCode()
   259  		if exit == 1 {
   260  			// resources does not exist
   261  			return true
   262  		}
   263  		content := session.Wait().Out.Contents()
   264  		// resource is marked for deletion
   265  		return len(content) > 0
   266  	}
   267  	Expect(deletedOrMarkedToDelete()).To(BeTrue())
   268  }
   269  
   270  // GetAnnotationsDeployment gets the annotations from the deployment
   271  // belonging to the given component, app and project
   272  func (kubectl KubectlRunner) GetAnnotationsDeployment(componentName, appName, projectName string) map[string]string {
   273  	return GetAnnotationsDeployment(kubectl.path, componentName, appName, projectName)
   274  }
   275  
   276  // GetAllPodsInNs gets the list of pods in given namespace. It waits for reasonable amount of time for pods to come up
   277  func (kubectl KubectlRunner) GetAllPodsInNs(namespace string) string {
   278  	args := []string{"get", ResourceTypePod, "-n", namespace}
   279  	noResourcesMsg := fmt.Sprintf("No resources found in %s namespace", namespace)
   280  	kubectl.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
   281  		return !strings.Contains(output, noResourcesMsg)
   282  	}, true)
   283  	return Cmd(kubectl.path, args...).ShouldPass().Out()
   284  }
   285  
   286  // GetAllPodNames gets the names of pods in given namespace
   287  func (kubectl KubectlRunner) GetAllPodNames(namespace string) []string {
   288  	session := CmdRunner(kubectl.path, "get", "pods", "--namespace", namespace, "-o", "jsonpath={.items[*].metadata.name}")
   289  	Eventually(session).Should(gexec.Exit(0))
   290  	output := string(session.Wait().Out.Contents())
   291  	if output == "" {
   292  		return []string{}
   293  	}
   294  	return strings.Split(output, " ")
   295  }
   296  
   297  func (kubectl KubectlRunner) PodsShouldBeRunning(project string, regex string) {
   298  	// now verify if the pods for the operator have started
   299  	pods := kubectl.GetAllPodsInNs(project)
   300  	// Look for pods with specified regex
   301  	pod := regexp.MustCompile(regex).FindString(pods)
   302  
   303  	args := []string{"get", ResourceTypePod, pod, "-o", "template=\"{{.status.phase}}\"", "-n", project}
   304  	kubectl.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
   305  		return strings.Contains(output, "Running")
   306  	})
   307  }
   308  
   309  // WaitForRunnerCmdOut runs "kubectl" command until it gets
   310  // the expected output.
   311  // It accepts 4 arguments
   312  // args (arguments to the program)
   313  // timeout (the time to wait for the output)
   314  // errOnFail (flag to set if test should fail if command fails)
   315  // check (function with output check logic)
   316  // It times out if the command doesn't fetch the
   317  // expected output  within the timeout period.
   318  func (kubectl KubectlRunner) WaitForRunnerCmdOut(args []string, timeout int, errOnFail bool, check func(output string) bool, includeStdErr ...bool) bool {
   319  	pingTimeout := time.After(time.Duration(timeout) * time.Minute)
   320  	// this is a test package so time.Tick() is acceptable
   321  	// nolint
   322  	tick := time.Tick(time.Second)
   323  	for {
   324  		select {
   325  		case <-pingTimeout:
   326  			Fail(fmt.Sprintf("Timeout after %v minutes", timeout))
   327  
   328  		case <-tick:
   329  			session := CmdRunner(kubectl.path, args...)
   330  			if errOnFail {
   331  				Eventually(session).Should(gexec.Exit(0), runningCmd(session.Command))
   332  			} else {
   333  				Eventually(session).Should(gexec.Exit(), runningCmd(session.Command))
   334  			}
   335  			session.Wait()
   336  			output := string(session.Out.Contents())
   337  
   338  			if len(includeStdErr) > 0 && includeStdErr[0] {
   339  				output += "\n"
   340  				output += string(session.Err.Contents())
   341  			}
   342  			if check(strings.TrimSpace(output)) {
   343  				return true
   344  			}
   345  		}
   346  	}
   347  }
   348  
   349  // CreateSecret takes secret name, password and the namespace where we want to create the specific secret into the cluster
   350  func (kubectl KubectlRunner) CreateSecret(secretName, secretPass, project string) {
   351  	Cmd(kubectl.path, "create", "secret", "generic", secretName, "--from-literal=password="+secretPass, "-n", project).ShouldPass()
   352  }
   353  
   354  // GetSecrets gets all the secrets belonging to the project
   355  func (kubectl KubectlRunner) GetSecrets(project string) string {
   356  	return GetSecrets(kubectl.path, project)
   357  }
   358  
   359  // GetEnvRefNames gets the ref values from the envFroms of the deployment belonging to the given data
   360  func (kubectl KubectlRunner) GetEnvRefNames(componentName, appName, projectName string) []string {
   361  	return GetEnvRefNames(kubectl.path, componentName, appName, projectName)
   362  }
   363  
   364  // GetEnvFromEntry returns envFrom entry of the deployment
   365  func (kubectl KubectlRunner) GetEnvFromEntry(componentName string, appName string, projectName string) string {
   366  	return GetEnvFromEntry(kubectl.path, componentName, appName, projectName)
   367  }
   368  
   369  // GetVolumeNamesFromDeployment gets the volumes from the deployment belonging to the given data
   370  func (kubectl KubectlRunner) GetVolumeNamesFromDeployment(componentName, appName, projectName string) map[string]string {
   371  	return GetVolumeNamesFromDeployment(kubectl.path, componentName, appName, projectName)
   372  }
   373  
   374  // add config map to the project for cleanup
   375  func (kubectl KubectlRunner) addConfigMapForCleanup(projectName string) {
   376  	Cmd(kubectl.path, "create", "configmap", "config-map-for-cleanup", "--from-literal", "type=testing", "--from-literal", "team=odo", "-n", projectName).ShouldPass()
   377  }
   378  
   379  // ScalePodToZero scales the pod of the deployment to zero.
   380  // It waits for the pod to get deleted from the cluster before returning
   381  func (kubectl KubectlRunner) ScalePodToZero(componentName, appName, projectName string) {
   382  	podName := kubectl.GetRunningPodNameByComponent(componentName, projectName)
   383  	Cmd(kubectl.path, "scale", "deploy", strings.Join([]string{componentName, appName}, "-"), "--replicas=0").ShouldPass()
   384  	kubectl.WaitForRunnerCmdOut([]string{"get", "-n", projectName, ResourceTypePod, podName}, 1, false, func(output string) bool {
   385  		return !strings.Contains(output, podName)
   386  	})
   387  }
   388  
   389  func (kubectl KubectlRunner) GetBindableKinds() (string, string) {
   390  	return Cmd(kubectl.path, "get", "bindablekinds", "bindable-kinds", "-ojsonpath='{.status[*].kind}'").ShouldRun().OutAndErr()
   391  }
   392  
   393  func (kubectl KubectlRunner) GetServiceBinding(name, projectName string) (string, string) {
   394  	return Cmd(kubectl.path, "get", "servicebinding", name, "-n", projectName).ShouldRun().OutAndErr()
   395  }
   396  
   397  func (kubectl KubectlRunner) EnsureOperatorIsInstalled(partialOperatorName string) {
   398  	WaitForCmdOut(kubectl.path, []string{"get", "csv", "-o", "jsonpath={.items[?(@.status.phase==\"Succeeded\")].metadata.name}"}, 4, true, func(output string) bool {
   399  		return strings.Contains(output, partialOperatorName)
   400  	})
   401  }
   402  
   403  func (kubectl KubectlRunner) EnsurePodIsUp(namespace, podName string) {
   404  	WaitForCmdOut(kubectl.path, []string{"get", "pods", "-n", namespace, "-o", "jsonpath='{range .items[*]}{.metadata.name}'"}, 4, true, func(output string) bool {
   405  		return strings.Contains(output, podName)
   406  	})
   407  }
   408  
   409  func (kubectl KubectlRunner) GetNamespaceProject() string {
   410  	return Cmd(kubectl.path, "get", "namespace").ShouldPass().Out()
   411  }
   412  
   413  func (kubectl KubectlRunner) HasNamespaceProject(name string) bool {
   414  	out := Cmd(kubectl.path, "get", "namespace", name, "-o", "jsonpath={.metadata.name}").
   415  		ShouldRun().Out()
   416  	return strings.Contains(out, name)
   417  }
   418  
   419  func (kubectl KubectlRunner) ListNamespaceProject(name string) {
   420  	Eventually(func() string {
   421  		return Cmd(kubectl.path, "get", "ns").ShouldRun().Out()
   422  	}, 30, 1).Should(ContainSubstring(name))
   423  }
   424  
   425  func (kubectl KubectlRunner) GetActiveNamespace() string {
   426  	return Cmd(kubectl.path, "config", "view", "--minify", "-ojsonpath={..namespace}").ShouldPass().Out()
   427  }
   428  
   429  func (kubectl KubectlRunner) GetAllNamespaceProjects() []string {
   430  	output := Cmd(kubectl.path, "get", "namespaces",
   431  		"-o", "custom-columns=NAME:.metadata.name",
   432  		"--no-headers").ShouldPass().Out()
   433  	result, err := ExtractLines(output)
   434  	Expect(err).ShouldNot(HaveOccurred())
   435  	return result
   436  }
   437  
   438  func (kubectl KubectlRunner) GetLogs(podName string) string {
   439  	output := Cmd(kubectl.path, "logs", podName).ShouldPass().Out()
   440  	return output
   441  }
   442  
   443  func (kubectl KubectlRunner) AssertContainsLabel(kind, namespace, componentName, appName, mode, key, value string) {
   444  	selector := labels.Builder().WithComponentName(componentName).WithAppName(appName).WithMode(mode).SelectorFlag()
   445  	all := Cmd(kubectl.path, "get", kind, selector, "-n", namespace, "-o", "jsonpath={.items[0].metadata.labels}").ShouldPass().Out()
   446  	Expect(all).To(ContainSubstring(fmt.Sprintf(`"%s":"%s"`, key, value)))
   447  }
   448  
   449  func (kubectl KubectlRunner) AssertNoContainsLabel(kind, namespace, componentName, appName, mode, key string) {
   450  	selector := labels.Builder().WithComponentName(componentName).WithAppName(appName).WithMode(mode).SelectorFlag()
   451  	all := Cmd(kubectl.path, "get", kind, selector, "-n", namespace, "-o", "jsonpath={.items[0].metadata.labels}").ShouldPass().Out()
   452  	Expect(all).ToNot(ContainSubstring(fmt.Sprintf(`"%s"`, key)))
   453  }
   454  
   455  func (kubectl KubectlRunner) AssertNonAuthenticated() {
   456  	// Nothing to do
   457  }
   458  
   459  func (kubectl KubectlRunner) GetVersion() string {
   460  	res := Cmd(kubectl.path, "version", "--output=json").ShouldPass().Out()
   461  	var js map[string]interface{}
   462  	err := json.Unmarshal([]byte(res), &js)
   463  	Expect(err).ShouldNot(HaveOccurred())
   464  	sv := js["serverVersion"].(map[string]interface{})
   465  	minor := sv["minor"].(string)
   466  	major := sv["major"].(string)
   467  	return major + "." + minor
   468  }
   469  
   470  func (kubectl KubectlRunner) SetLabelsOnNamespace(ns string, labelValues ...string) {
   471  	args := []string{"label", "namespaces", ns}
   472  	args = append(args, labelValues...)
   473  	Cmd(kubectl.path, args...).ShouldPass()
   474  }
   475  

View as plain text