utils.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*
  2. Copyright 2026 LocoStack.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package utils
  14. import (
  15. "bufio"
  16. "bytes"
  17. "fmt"
  18. "os"
  19. "os/exec"
  20. "strings"
  21. . "github.com/onsi/ginkgo/v2" // nolint:revive,staticcheck
  22. )
  23. const (
  24. certmanagerVersion = "v1.20.0"
  25. certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml"
  26. defaultKindBinary = "kind"
  27. defaultKindCluster = "kind"
  28. )
  29. func warnError(err error) {
  30. _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err)
  31. }
  32. // Run executes the provided command within this context
  33. func Run(cmd *exec.Cmd) (string, error) {
  34. dir, _ := GetProjectDir()
  35. cmd.Dir = dir
  36. if err := os.Chdir(cmd.Dir); err != nil {
  37. _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err)
  38. }
  39. cmd.Env = append(os.Environ(), "GO111MODULE=on")
  40. command := strings.Join(cmd.Args, " ")
  41. _, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command)
  42. output, err := cmd.CombinedOutput()
  43. if err != nil {
  44. return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err)
  45. }
  46. return string(output), nil
  47. }
  48. // UninstallCertManager uninstalls the cert manager
  49. func UninstallCertManager() {
  50. url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
  51. cmd := exec.Command("kubectl", "delete", "-f", url)
  52. if _, err := Run(cmd); err != nil {
  53. warnError(err)
  54. }
  55. // Delete leftover leases in kube-system (not cleaned by default)
  56. kubeSystemLeases := []string{
  57. "cert-manager-cainjector-leader-election",
  58. "cert-manager-controller",
  59. }
  60. for _, lease := range kubeSystemLeases {
  61. cmd = exec.Command("kubectl", "delete", "lease", lease,
  62. "-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0")
  63. if _, err := Run(cmd); err != nil {
  64. warnError(err)
  65. }
  66. }
  67. }
  68. // InstallCertManager installs the cert manager bundle.
  69. func InstallCertManager() error {
  70. url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
  71. cmd := exec.Command("kubectl", "apply", "-f", url)
  72. if _, err := Run(cmd); err != nil {
  73. return err
  74. }
  75. // Wait for cert-manager-webhook to be ready, which can take time if cert-manager
  76. // was re-installed after uninstalling on a cluster.
  77. cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook",
  78. "--for", "condition=Available",
  79. "--namespace", "cert-manager",
  80. "--timeout", "5m",
  81. )
  82. _, err := Run(cmd)
  83. return err
  84. }
  85. // IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed
  86. // by verifying the existence of key CRDs related to Cert Manager.
  87. func IsCertManagerCRDsInstalled() bool {
  88. // List of common Cert Manager CRDs
  89. certManagerCRDs := []string{
  90. "certificates.cert-manager.io",
  91. "issuers.cert-manager.io",
  92. "clusterissuers.cert-manager.io",
  93. "certificaterequests.cert-manager.io",
  94. "orders.acme.cert-manager.io",
  95. "challenges.acme.cert-manager.io",
  96. }
  97. // Execute the kubectl command to get all CRDs
  98. cmd := exec.Command("kubectl", "get", "crds")
  99. output, err := Run(cmd)
  100. if err != nil {
  101. return false
  102. }
  103. // Check if any of the Cert Manager CRDs are present
  104. crdList := GetNonEmptyLines(output)
  105. for _, crd := range certManagerCRDs {
  106. for _, line := range crdList {
  107. if strings.Contains(line, crd) {
  108. return true
  109. }
  110. }
  111. }
  112. return false
  113. }
  114. // LoadImageToKindClusterWithName loads a local docker image to the kind cluster
  115. func LoadImageToKindClusterWithName(name string) error {
  116. cluster := defaultKindCluster
  117. if v, ok := os.LookupEnv("KIND_CLUSTER"); ok {
  118. cluster = v
  119. }
  120. kindOptions := []string{"load", "docker-image", name, "--name", cluster}
  121. kindBinary := defaultKindBinary
  122. if v, ok := os.LookupEnv("KIND"); ok {
  123. kindBinary = v
  124. }
  125. cmd := exec.Command(kindBinary, kindOptions...)
  126. _, err := Run(cmd)
  127. return err
  128. }
  129. // GetNonEmptyLines converts given command output string into individual objects
  130. // according to line breakers, and ignores the empty elements in it.
  131. func GetNonEmptyLines(output string) []string {
  132. var res []string
  133. elements := strings.SplitSeq(output, "\n")
  134. for element := range elements {
  135. if element != "" {
  136. res = append(res, element)
  137. }
  138. }
  139. return res
  140. }
  141. // GetProjectDir will return the directory where the project is
  142. func GetProjectDir() (string, error) {
  143. wd, err := os.Getwd()
  144. if err != nil {
  145. return wd, fmt.Errorf("failed to get current working directory: %w", err)
  146. }
  147. wd = strings.ReplaceAll(wd, "/test/e2e", "")
  148. return wd, nil
  149. }
  150. // UncommentCode searches for target in the file and remove the comment prefix
  151. // of the target content. The target content may span multiple lines.
  152. func UncommentCode(filename, target, prefix string) error {
  153. // false positive
  154. // nolint:gosec
  155. content, err := os.ReadFile(filename)
  156. if err != nil {
  157. return fmt.Errorf("failed to read file %q: %w", filename, err)
  158. }
  159. strContent := string(content)
  160. idx := strings.Index(strContent, target)
  161. if idx < 0 {
  162. return fmt.Errorf("unable to find the code %q to be uncommented", target)
  163. }
  164. out := new(bytes.Buffer)
  165. _, err = out.Write(content[:idx])
  166. if err != nil {
  167. return fmt.Errorf("failed to write to output: %w", err)
  168. }
  169. scanner := bufio.NewScanner(bytes.NewBufferString(target))
  170. if !scanner.Scan() {
  171. return nil
  172. }
  173. for {
  174. if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil {
  175. return fmt.Errorf("failed to write to output: %w", err)
  176. }
  177. // Avoid writing a newline in case the previous line was the last in target.
  178. if !scanner.Scan() {
  179. break
  180. }
  181. if _, err = out.WriteString("\n"); err != nil {
  182. return fmt.Errorf("failed to write to output: %w", err)
  183. }
  184. }
  185. if _, err = out.Write(content[idx+len(target):]); err != nil {
  186. return fmt.Errorf("failed to write to output: %w", err)
  187. }
  188. // false positive
  189. // nolint:gosec
  190. if err = os.WriteFile(filename, out.Bytes(), 0644); err != nil {
  191. return fmt.Errorf("failed to write file %q: %w", filename, err)
  192. }
  193. return nil
  194. }