eso_argocd_application.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. Copyright © The ESO Authors
  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. https://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 addon
  14. import (
  15. "context"
  16. "fmt"
  17. "strings"
  18. "time"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  21. "k8s.io/apimachinery/pkg/runtime/schema"
  22. "k8s.io/apimachinery/pkg/util/wait"
  23. "k8s.io/client-go/dynamic"
  24. "sigs.k8s.io/yaml"
  25. . "github.com/onsi/ginkgo/v2"
  26. )
  27. // HelmChart installs the specified Chart into the cluster.
  28. type ArgoCDApplication struct {
  29. Name string
  30. Namespace string
  31. DestinationNamespace string
  32. HelmChart string
  33. HelmRepo string
  34. HelmRevision string
  35. HelmParameters []string
  36. config *Config
  37. dc *dynamic.DynamicClient
  38. }
  39. var argoApp = schema.GroupVersionResource{
  40. Group: "argoproj.io",
  41. Version: "v1alpha1",
  42. Resource: "applications",
  43. }
  44. var argoAppResource = `apiVersion: argoproj.io/v1alpha1
  45. kind: Application
  46. metadata:
  47. name: %s
  48. namespace: %s
  49. annotations:
  50. argocd.argoproj.io/refresh: "hard"
  51. spec:
  52. project: default
  53. syncPolicy:
  54. automated:
  55. prune: true
  56. selfHeal: true
  57. syncOptions:
  58. - CreateNamespace=true
  59. - ServerSideApply=true
  60. source:
  61. chart: %s
  62. repoURL: %s
  63. targetRevision: %s
  64. helm:
  65. releaseName: %s
  66. parameters: %s
  67. destination:
  68. server: https://kubernetes.default.svc
  69. namespace: %s
  70. `
  71. const (
  72. // taken from: https://github.com/argoproj/argo-cd/blob/0a8a71e12c5010d5ada1fce37feb0d8add1c61d0/pkg/apis/application/v1alpha1/types.go#LL1351C41-L1351C47
  73. StatusSynced = "Synced"
  74. )
  75. // Setup stores the config in an internal field
  76. // to get access to the k8s api in orderto fetch logs.
  77. func (c *ArgoCDApplication) Setup(cfg *Config) error {
  78. c.config = cfg
  79. dc, err := dynamic.NewForConfig(cfg.KubeConfig)
  80. if err != nil {
  81. return err
  82. }
  83. c.dc = dc
  84. return nil
  85. }
  86. // Install adds the chart repo and installs the helm chart.
  87. func (c *ArgoCDApplication) Install() error {
  88. // construct helm parameters
  89. var helmParams string
  90. for _, param := range c.HelmParameters {
  91. args := strings.Split(param, "=")
  92. helmParams += fmt.Sprintf(`
  93. - name: "%s"
  94. value: %s`, args[0], args[1])
  95. }
  96. jsonBytes, err := yaml.YAMLToJSON([]byte(fmt.Sprintf(argoAppResource, c.Name, c.Namespace, c.HelmChart, c.HelmRepo, c.HelmRevision, c.Name, helmParams, c.DestinationNamespace)))
  97. if err != nil {
  98. return fmt.Errorf("unable to decode argo app yaml to json: %w", err)
  99. }
  100. us := &unstructured.Unstructured{}
  101. err = us.UnmarshalJSON(jsonBytes)
  102. if err != nil {
  103. return fmt.Errorf("unable to unmarshal json into unstructured: %w", err)
  104. }
  105. _, err = c.dc.Resource(argoApp).Namespace(c.Namespace).Create(GinkgoT().Context(), us, metav1.CreateOptions{})
  106. if err != nil {
  107. return fmt.Errorf("unable to create argo app: %w", err)
  108. }
  109. // wait for app to become ready
  110. err = wait.PollUntilContextTimeout(GinkgoT().Context(), time.Second*5, time.Minute*10, true, func(ctx context.Context) (bool, error) {
  111. us, err = c.dc.Resource(argoApp).Namespace(c.Namespace).Get(ctx, c.Name, metav1.GetOptions{})
  112. if err != nil {
  113. return false, err
  114. }
  115. syncStatus, _, err := unstructured.NestedString(us.Object, "status", "sync", "status")
  116. if err != nil {
  117. return false, nil
  118. }
  119. return syncStatus == StatusSynced, nil
  120. })
  121. if err != nil {
  122. return fmt.Errorf("failed waiting for argo app to become ready: %w", err)
  123. }
  124. return waitForExternalSecretWebhookReady(webhookServiceName(c.Name), c.DestinationNamespace)
  125. }
  126. // Uninstall removes the chart aswell as the repo.
  127. func (c *ArgoCDApplication) Uninstall() error {
  128. err := c.dc.Resource(argoApp).Namespace(c.Namespace).Delete(GinkgoT().Context(), c.Name, metav1.DeleteOptions{})
  129. if err != nil {
  130. return err
  131. }
  132. err = uninstallCRDs(c.config)
  133. if err != nil {
  134. return err
  135. }
  136. return nil
  137. }
  138. func (c *ArgoCDApplication) Logs() error {
  139. return nil
  140. }