helpers.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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 v2
  14. import (
  15. "context"
  16. "fmt"
  17. "time"
  18. corev1 "k8s.io/api/core/v1"
  19. rbacv1 "k8s.io/api/rbac/v1"
  20. apierrors "k8s.io/apimachinery/pkg/api/errors"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/types"
  23. "k8s.io/apimachinery/pkg/util/wait"
  24. "k8s.io/client-go/util/retry"
  25. "sigs.k8s.io/controller-runtime/pkg/client"
  26. "github.com/external-secrets/external-secrets-e2e/framework"
  27. "github.com/external-secrets/external-secrets-e2e/framework/log"
  28. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  29. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  30. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  31. . "github.com/onsi/gomega"
  32. )
  33. const (
  34. ProviderNamespace = "external-secrets-system"
  35. DefaultSAName = "default"
  36. providerStoreReady = "Ready"
  37. )
  38. func ProviderAddress(providerName string) string {
  39. return ProviderAddressInNamespace(providerName, ProviderNamespace)
  40. }
  41. func ProviderAddressInNamespace(providerName, namespace string) string {
  42. return fmt.Sprintf("provider-%s.%s.svc:8080", providerName, namespace)
  43. }
  44. func GetClusterCABundle(f *framework.Framework, namespace string) []byte {
  45. var caBundle []byte
  46. krc := &corev1.ConfigMap{}
  47. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  48. defer cancel()
  49. err := wait.PollUntilContextTimeout(ctx, 250*time.Millisecond, 30*time.Second, true, func(ctx context.Context) (bool, error) {
  50. if err := f.CRClient.Get(ctx, types.NamespacedName{Name: "kube-root-ca.crt", Namespace: namespace}, krc); err != nil {
  51. if apierrors.IsNotFound(err) {
  52. return false, nil
  53. }
  54. return false, err
  55. }
  56. caBundle = []byte(krc.Data["ca.crt"])
  57. return len(caBundle) > 0, nil
  58. })
  59. Expect(err).NotTo(HaveOccurred())
  60. return caBundle
  61. }
  62. func CreateKubernetesAccessRole(f *framework.Framework, name, serviceAccountName, serviceAccountNamespace, remoteNamespace string) {
  63. role := &rbacv1.Role{
  64. ObjectMeta: metav1.ObjectMeta{
  65. Name: name,
  66. Namespace: remoteNamespace,
  67. },
  68. Rules: []rbacv1.PolicyRule{
  69. {
  70. APIGroups: []string{""},
  71. Resources: []string{"secrets"},
  72. Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
  73. },
  74. {
  75. APIGroups: []string{"authorization.k8s.io"},
  76. Resources: []string{"selfsubjectrulesreviews", "selfsubjectaccessreviews"},
  77. Verbs: []string{"create"},
  78. },
  79. },
  80. }
  81. Expect(createOrIgnoreAlreadyExists(f, role)).To(Succeed())
  82. roleBinding := &rbacv1.RoleBinding{
  83. ObjectMeta: metav1.ObjectMeta{
  84. Name: name,
  85. Namespace: remoteNamespace,
  86. },
  87. Subjects: []rbacv1.Subject{
  88. {
  89. Kind: "ServiceAccount",
  90. Name: serviceAccountName,
  91. Namespace: serviceAccountNamespace,
  92. },
  93. },
  94. RoleRef: rbacv1.RoleRef{
  95. APIGroup: "rbac.authorization.k8s.io",
  96. Kind: "Role",
  97. Name: name,
  98. },
  99. }
  100. Expect(createOrIgnoreAlreadyExists(f, roleBinding)).To(Succeed())
  101. }
  102. func NewKubernetesStoreProvider(remoteNamespace, serviceAccountName string, serviceAccountNamespace *string, caBundle []byte) *esv1.SecretStoreProvider {
  103. return &esv1.SecretStoreProvider{
  104. Kubernetes: &esv1.KubernetesProvider{
  105. Server: esv1.KubernetesServer{
  106. URL: "https://kubernetes.default.svc",
  107. CABundle: caBundle,
  108. },
  109. RemoteNamespace: remoteNamespace,
  110. Auth: &esv1.KubernetesAuth{
  111. ServiceAccount: &esmeta.ServiceAccountSelector{
  112. Name: serviceAccountName,
  113. Namespace: serviceAccountNamespace,
  114. },
  115. },
  116. },
  117. }
  118. }
  119. func runtimeClassName(name string) string {
  120. return fmt.Sprintf("%s-runtime", name)
  121. }
  122. func ensureClusterProviderClass(f *framework.Framework, name, address string) *esv1alpha1.ClusterProviderClass {
  123. runtimeClass := &esv1alpha1.ClusterProviderClass{
  124. ObjectMeta: metav1.ObjectMeta{
  125. Name: name,
  126. },
  127. Spec: esv1alpha1.ClusterProviderClassSpec{
  128. Address: address,
  129. },
  130. }
  131. Expect(createOrIgnoreAlreadyExists(f, runtimeClass)).To(Succeed())
  132. log.Logf("created ClusterProviderClass: %s", name)
  133. return runtimeClass
  134. }
  135. func ensureProviderClass(f *framework.Framework, namespace, name, address string) *esv1alpha1.ProviderClass {
  136. runtimeClass := &esv1alpha1.ProviderClass{
  137. ObjectMeta: metav1.ObjectMeta{
  138. Name: name,
  139. Namespace: namespace,
  140. },
  141. Spec: esv1alpha1.ProviderClassSpec{
  142. Address: address,
  143. },
  144. }
  145. Expect(createOrIgnoreAlreadyExists(f, runtimeClass)).To(Succeed())
  146. log.Logf("created ProviderClass: %s/%s", namespace, name)
  147. return runtimeClass
  148. }
  149. func deepCopyClusterStoreConditions(conditions []esv1.ClusterSecretStoreCondition) []esv1.ClusterSecretStoreCondition {
  150. if len(conditions) == 0 {
  151. return nil
  152. }
  153. out := make([]esv1.ClusterSecretStoreCondition, 0, len(conditions))
  154. for _, condition := range conditions {
  155. out = append(out, *condition.DeepCopy())
  156. }
  157. return out
  158. }
  159. func copyStoreProviderRef(providerRef *esv1.StoreProviderRef) *esv1.StoreProviderRef {
  160. if providerRef == nil {
  161. return nil
  162. }
  163. copy := *providerRef
  164. return &copy
  165. }
  166. func CreateRuntimeSecretStore(f *framework.Framework, namespace, name, address string, providerRef *esv1.StoreProviderRef) *esv1.SecretStore {
  167. runtimeClass := ensureProviderClass(f, namespace, runtimeClassName(name), address)
  168. store := &esv1.SecretStore{
  169. ObjectMeta: metav1.ObjectMeta{
  170. Name: name,
  171. Namespace: namespace,
  172. },
  173. Spec: esv1.SecretStoreSpec{
  174. RuntimeRef: &esv1.StoreRuntimeRef{
  175. Name: runtimeClass.Name,
  176. },
  177. ProviderRef: copyStoreProviderRef(providerRef),
  178. },
  179. }
  180. Expect(createOrIgnoreAlreadyExists(f, store)).To(Succeed())
  181. log.Logf("created SecretStore: %s/%s", namespace, name)
  182. return store
  183. }
  184. func CreateRuntimeClusterSecretStore(f *framework.Framework, name, address string, providerRef *esv1.StoreProviderRef, conditions []esv1.ClusterSecretStoreCondition) *esv1.ClusterSecretStore {
  185. runtimeClass := ensureClusterProviderClass(f, runtimeClassName(name), address)
  186. store := &esv1.ClusterSecretStore{
  187. ObjectMeta: metav1.ObjectMeta{
  188. Name: name,
  189. },
  190. Spec: esv1.SecretStoreSpec{
  191. Conditions: deepCopyClusterStoreConditions(conditions),
  192. RuntimeRef: &esv1.StoreRuntimeRef{
  193. Name: runtimeClass.Name,
  194. },
  195. ProviderRef: copyStoreProviderRef(providerRef),
  196. },
  197. }
  198. Expect(createOrIgnoreAlreadyExists(f, store)).To(Succeed())
  199. log.Logf("created ClusterSecretStore: %s", name)
  200. return store
  201. }
  202. func WaitForSecretStoreReady(f *framework.Framework, namespace, name string, timeout time.Duration) *esv1.SecretStore {
  203. return WaitForSecretStoreCondition(f, namespace, name, metav1.ConditionTrue, timeout)
  204. }
  205. func WaitForSecretStoreNotReady(f *framework.Framework, namespace, name string, timeout time.Duration) *esv1.SecretStore {
  206. return WaitForSecretStoreCondition(f, namespace, name, metav1.ConditionFalse, timeout)
  207. }
  208. func WaitForSecretStoreCondition(f *framework.Framework, namespace, name string, status metav1.ConditionStatus, timeout time.Duration) *esv1.SecretStore {
  209. store := &esv1.SecretStore{}
  210. Eventually(func() bool {
  211. err := f.CRClient.Get(context.Background(), types.NamespacedName{
  212. Name: name,
  213. Namespace: namespace,
  214. }, store)
  215. if err != nil {
  216. log.Logf("failed to get SecretStore: %v", err)
  217. return false
  218. }
  219. return hasSecretStoreReadyConditionStatus(store.Status.Conditions, status)
  220. }, timeout, time.Second).Should(BeTrue(), fmt.Sprintf("SecretStore should become %s", status))
  221. return store
  222. }
  223. func WaitForClusterSecretStoreReady(f *framework.Framework, name string, timeout time.Duration) *esv1.ClusterSecretStore {
  224. return WaitForClusterSecretStoreCondition(f, name, metav1.ConditionTrue, timeout)
  225. }
  226. func WaitForClusterSecretStoreNotReady(f *framework.Framework, name string, timeout time.Duration) *esv1.ClusterSecretStore {
  227. return WaitForClusterSecretStoreCondition(f, name, metav1.ConditionFalse, timeout)
  228. }
  229. func WaitForClusterSecretStoreCondition(f *framework.Framework, name string, status metav1.ConditionStatus, timeout time.Duration) *esv1.ClusterSecretStore {
  230. store := &esv1.ClusterSecretStore{}
  231. Eventually(func() bool {
  232. err := f.CRClient.Get(context.Background(), types.NamespacedName{Name: name}, store)
  233. if err != nil {
  234. log.Logf("failed to get ClusterSecretStore: %v", err)
  235. return false
  236. }
  237. return hasSecretStoreReadyConditionStatus(store.Status.Conditions, status)
  238. }, timeout, time.Second).Should(BeTrue(), fmt.Sprintf("ClusterSecretStore should become %s", status))
  239. return store
  240. }
  241. func hasSecretStoreReadyConditionStatus(conditions []esv1.SecretStoreStatusCondition, status metav1.ConditionStatus) bool {
  242. for _, condition := range conditions {
  243. if string(condition.Type) == providerStoreReady && string(condition.Status) == string(status) {
  244. return true
  245. }
  246. }
  247. return false
  248. }
  249. func createOrIgnoreAlreadyExists(f *framework.Framework, obj client.Object) error {
  250. err := f.CRClient.Create(context.Background(), obj)
  251. if err == nil {
  252. return nil
  253. }
  254. if !apierrors.IsAlreadyExists(err) {
  255. return err
  256. }
  257. return retry.RetryOnConflict(retry.DefaultRetry, func() error {
  258. existing := obj.DeepCopyObject().(client.Object)
  259. if err := f.CRClient.Get(context.Background(), client.ObjectKeyFromObject(obj), existing); err != nil {
  260. return err
  261. }
  262. obj.SetResourceVersion(existing.GetResourceVersion())
  263. obj.SetUID(existing.GetUID())
  264. return f.CRClient.Update(context.Background(), obj)
  265. })
  266. }