/ kubernetes / test / utils / utils.go
utils.go
  1  // Copyright 2025 Alibaba Group Holding Ltd.
  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 utils
 16  
 17  import (
 18  	"bufio"
 19  	"bytes"
 20  	"fmt"
 21  	"os"
 22  	"os/exec"
 23  	"strings"
 24  
 25  	. "github.com/onsi/ginkgo/v2" // nolint:revive,staticcheck
 26  )
 27  
 28  const (
 29  	prometheusOperatorVersion = "v0.77.1"
 30  	prometheusOperatorURL     = "https://github.com/prometheus-operator/prometheus-operator/" +
 31  		"releases/download/%s/bundle.yaml"
 32  
 33  	certmanagerVersion = "v1.16.3"
 34  	certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml"
 35  )
 36  
 37  func warnError(err error) {
 38  	_, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err)
 39  }
 40  
 41  // Run executes the provided command within this context
 42  func Run(cmd *exec.Cmd) (string, error) {
 43  	dir, _ := GetProjectDir()
 44  	cmd.Dir = dir
 45  
 46  	if err := os.Chdir(cmd.Dir); err != nil {
 47  		_, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err)
 48  	}
 49  
 50  	cmd.Env = append(os.Environ(), "GO111MODULE=on")
 51  	command := strings.Join(cmd.Args, " ")
 52  	_, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command)
 53  	output, err := cmd.CombinedOutput()
 54  	if err != nil {
 55  		return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err)
 56  	}
 57  
 58  	return string(output), nil
 59  }
 60  
 61  // InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics.
 62  func InstallPrometheusOperator() error {
 63  	url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
 64  	cmd := exec.Command("kubectl", "create", "-f", url)
 65  	_, err := Run(cmd)
 66  	return err
 67  }
 68  
 69  // UninstallPrometheusOperator uninstalls the prometheus
 70  func UninstallPrometheusOperator() {
 71  	url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
 72  	cmd := exec.Command("kubectl", "delete", "-f", url)
 73  	if _, err := Run(cmd); err != nil {
 74  		warnError(err)
 75  	}
 76  }
 77  
 78  // IsPrometheusCRDsInstalled checks if any Prometheus CRDs are installed
 79  // by verifying the existence of key CRDs related to Prometheus.
 80  func IsPrometheusCRDsInstalled() bool {
 81  	// List of common Prometheus CRDs
 82  	prometheusCRDs := []string{
 83  		"prometheuses.monitoring.coreos.com",
 84  		"prometheusrules.monitoring.coreos.com",
 85  		"prometheusagents.monitoring.coreos.com",
 86  	}
 87  
 88  	cmd := exec.Command("kubectl", "get", "crds", "-o", "custom-columns=NAME:.metadata.name")
 89  	output, err := Run(cmd)
 90  	if err != nil {
 91  		return false
 92  	}
 93  	crdList := GetNonEmptyLines(output)
 94  	for _, crd := range prometheusCRDs {
 95  		for _, line := range crdList {
 96  			if strings.Contains(line, crd) {
 97  				return true
 98  			}
 99  		}
100  	}
101  
102  	return false
103  }
104  
105  // UninstallCertManager uninstalls the cert manager
106  func UninstallCertManager() {
107  	url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
108  	cmd := exec.Command("kubectl", "delete", "-f", url)
109  	if _, err := Run(cmd); err != nil {
110  		warnError(err)
111  	}
112  }
113  
114  // InstallCertManager installs the cert manager bundle.
115  func InstallCertManager() error {
116  	url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
117  	cmd := exec.Command("kubectl", "apply", "-f", url)
118  	if _, err := Run(cmd); err != nil {
119  		return err
120  	}
121  	// Wait for cert-manager-webhook to be ready, which can take time if cert-manager
122  	// was re-installed after uninstalling on a cluster.
123  	cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook",
124  		"--for", "condition=Available",
125  		"--namespace", "cert-manager",
126  		"--timeout", "5m",
127  	)
128  
129  	_, err := Run(cmd)
130  	return err
131  }
132  
133  // IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed
134  // by verifying the existence of key CRDs related to Cert Manager.
135  func IsCertManagerCRDsInstalled() bool {
136  	// List of common Cert Manager CRDs
137  	certManagerCRDs := []string{
138  		"certificates.cert-manager.io",
139  		"issuers.cert-manager.io",
140  		"clusterissuers.cert-manager.io",
141  		"certificaterequests.cert-manager.io",
142  		"orders.acme.cert-manager.io",
143  		"challenges.acme.cert-manager.io",
144  	}
145  
146  	// Execute the kubectl command to get all CRDs
147  	cmd := exec.Command("kubectl", "get", "crds")
148  	output, err := Run(cmd)
149  	if err != nil {
150  		return false
151  	}
152  
153  	// Check if any of the Cert Manager CRDs are present
154  	crdList := GetNonEmptyLines(output)
155  	for _, crd := range certManagerCRDs {
156  		for _, line := range crdList {
157  			if strings.Contains(line, crd) {
158  				return true
159  			}
160  		}
161  	}
162  
163  	return false
164  }
165  
166  // LoadImageToKindClusterWithName loads a local docker image to the kind cluster
167  func LoadImageToKindClusterWithName(name string) error {
168  	cluster := "kind"
169  	if v, ok := os.LookupEnv("KIND_CLUSTER"); ok {
170  		cluster = v
171  	}
172  	kindOptions := []string{"load", "docker-image", name, "--name", cluster}
173  	cmd := exec.Command("kind", kindOptions...)
174  	_, err := Run(cmd)
175  	return err
176  }
177  
178  // GetNonEmptyLines converts given command output string into individual objects
179  // according to line breakers, and ignores the empty elements in it.
180  func GetNonEmptyLines(output string) []string {
181  	var res []string
182  	elements := strings.Split(output, "\n")
183  	for _, element := range elements {
184  		if element != "" {
185  			res = append(res, element)
186  		}
187  	}
188  
189  	return res
190  }
191  
192  // GetProjectDir will return the directory where the project is
193  func GetProjectDir() (string, error) {
194  	wd, err := os.Getwd()
195  	if err != nil {
196  		return wd, fmt.Errorf("failed to get current working directory: %w", err)
197  	}
198  	wd = strings.ReplaceAll(wd, "/test/e2e", "")
199  	return wd, nil
200  }
201  
202  // UncommentCode searches for target in the file and remove the comment prefix
203  // of the target content. The target content may span multiple lines.
204  func UncommentCode(filename, target, prefix string) error {
205  	// false positive
206  	// nolint:gosec
207  	content, err := os.ReadFile(filename)
208  	if err != nil {
209  		return fmt.Errorf("failed to read file %q: %w", filename, err)
210  	}
211  	strContent := string(content)
212  
213  	idx := strings.Index(strContent, target)
214  	if idx < 0 {
215  		return fmt.Errorf("unable to find the code %q to be uncomment", target)
216  	}
217  
218  	out := new(bytes.Buffer)
219  	_, err = out.Write(content[:idx])
220  	if err != nil {
221  		return fmt.Errorf("failed to write to output: %w", err)
222  	}
223  
224  	scanner := bufio.NewScanner(bytes.NewBufferString(target))
225  	if !scanner.Scan() {
226  		return nil
227  	}
228  	for {
229  		if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil {
230  			return fmt.Errorf("failed to write to output: %w", err)
231  		}
232  		// Avoid writing a newline in case the previous line was the last in target.
233  		if !scanner.Scan() {
234  			break
235  		}
236  		if _, err = out.WriteString("\n"); err != nil {
237  			return fmt.Errorf("failed to write to output: %w", err)
238  		}
239  	}
240  
241  	if _, err = out.Write(content[idx+len(target):]); err != nil {
242  		return fmt.Errorf("failed to write to output: %w", err)
243  	}
244  
245  	// false positive
246  	// nolint:gosec
247  	if err = os.WriteFile(filename, out.Bytes(), 0644); err != nil {
248  		return fmt.Errorf("failed to write file %q: %w", filename, err)
249  	}
250  
251  	return nil
252  }