clusterpushsecret_controller_test.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package clusterpushsecret
  13. import (
  14. "context"
  15. "fmt"
  16. "math/rand"
  17. "sort"
  18. "time"
  19. v1 "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/types"
  22. crclient "sigs.k8s.io/controller-runtime/pkg/client"
  23. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  24. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  25. "github.com/external-secrets/external-secrets/pkg/controllers/clusterpushsecret/cpsmetrics"
  26. ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
  27. "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
  28. . "github.com/onsi/ginkgo/v2"
  29. . "github.com/onsi/gomega"
  30. )
  31. func init() {
  32. ctrlmetrics.SetUpLabelNames(false)
  33. cpsmetrics.SetUpMetrics()
  34. fakeProvider = fake.New()
  35. esv1.ForceRegister(fakeProvider, &esv1.SecretStoreProvider{
  36. Fake: &esv1.FakeProvider{},
  37. }, esv1.MaintenanceStatusMaintained)
  38. }
  39. var (
  40. secretName = "test-secret"
  41. testPushSecret = "test-ps"
  42. newPushSecret = "new-ps-name"
  43. defaultKey = "default-key"
  44. defaultVal = "default-value"
  45. testLabelKey = "test-label-key"
  46. testLabelValue = "test-label-value"
  47. testAnnotationKey = "test-annotation-key"
  48. testAnnotationValue = "test-annotation-value"
  49. updateStoreName = "updated-test-store"
  50. kubernetesMetadataLabel = "kubernetes.io/metadata.name"
  51. noneMatchingAnnotationKey = "no-longer-match-label-key"
  52. noneMatchingAnnotationVal = "no-longer-match-annotation-value"
  53. fakeProvider *fake.Client
  54. timeout = time.Second * 10
  55. interval = time.Millisecond * 250
  56. )
  57. type clusterPushSecretTestCase struct {
  58. namespaces []v1.Namespace
  59. clusterPushSecret func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret
  60. sourceSecret func(namespaces []v1.Namespace) []v1.Secret
  61. beforeCheck func(ctx context.Context, namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret)
  62. expectedClusterPushSecret func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret
  63. expectedPushSecrets func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret
  64. }
  65. var _ = Describe("ClusterPushSecret controller", func() {
  66. defaultClusterPushSecret := func() *v1alpha1.ClusterPushSecret {
  67. return &v1alpha1.ClusterPushSecret{
  68. ObjectMeta: metav1.ObjectMeta{
  69. Name: fmt.Sprintf("test-pes-%s", randString(10)),
  70. },
  71. Spec: v1alpha1.ClusterPushSecretSpec{
  72. PushSecretSpec: v1alpha1.PushSecretSpec{
  73. RefreshInterval: &metav1.Duration{Duration: time.Hour},
  74. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  75. {
  76. Name: "test-store",
  77. Kind: "SecretStore",
  78. },
  79. },
  80. Selector: v1alpha1.PushSecretSelector{
  81. Secret: &v1alpha1.PushSecretSecret{
  82. Name: secretName,
  83. },
  84. },
  85. Data: []v1alpha1.PushSecretData{
  86. {
  87. Match: v1alpha1.PushSecretMatch{},
  88. Metadata: nil,
  89. },
  90. },
  91. },
  92. },
  93. }
  94. }
  95. defaultSourceSecret := func(namespaces []v1.Namespace) []v1.Secret {
  96. var result []v1.Secret
  97. for _, s := range namespaces {
  98. result = append(result, v1.Secret{
  99. ObjectMeta: metav1.ObjectMeta{
  100. Name: secretName,
  101. Namespace: s.Name,
  102. },
  103. Data: map[string][]byte{
  104. defaultKey: []byte(defaultVal),
  105. },
  106. })
  107. }
  108. return result
  109. }
  110. DescribeTable("When reconciling a ClusterPush Secret",
  111. func(tc clusterPushSecretTestCase) {
  112. ctx := context.Background()
  113. By("creating namespaces")
  114. var namespaces []v1.Namespace
  115. for _, ns := range tc.namespaces {
  116. err := k8sClient.Create(ctx, &ns)
  117. Expect(err).ShouldNot(HaveOccurred())
  118. namespaces = append(namespaces, ns)
  119. }
  120. for _, s := range tc.sourceSecret(namespaces) {
  121. By("creating a source secret")
  122. err := k8sClient.Create(ctx, &s)
  123. Expect(err).ShouldNot(HaveOccurred())
  124. }
  125. By("creating a cluster push secret")
  126. pes := tc.clusterPushSecret(tc.namespaces)
  127. err := k8sClient.Create(ctx, &pes)
  128. Expect(err).ShouldNot(HaveOccurred())
  129. By("running before check")
  130. if tc.beforeCheck != nil {
  131. tc.beforeCheck(ctx, namespaces, pes)
  132. }
  133. // the before check above may have updated the namespaces, so refresh them
  134. for i, ns := range namespaces {
  135. err := k8sClient.Get(ctx, types.NamespacedName{Name: ns.Name}, &ns)
  136. Expect(err).ShouldNot(HaveOccurred())
  137. namespaces[i] = ns
  138. }
  139. By("checking the cluster push secret")
  140. expectedCPS := tc.expectedClusterPushSecret(namespaces, pes)
  141. Eventually(func(g Gomega) {
  142. key := types.NamespacedName{Name: expectedCPS.Name}
  143. var gotCes v1alpha1.ClusterPushSecret
  144. err = k8sClient.Get(ctx, key, &gotCes)
  145. g.Expect(err).ShouldNot(HaveOccurred())
  146. g.Expect(gotCes.Labels).To(Equal(expectedCPS.Labels))
  147. g.Expect(gotCes.Annotations).To(Equal(expectedCPS.Annotations))
  148. g.Expect(gotCes.Spec).To(Equal(expectedCPS.Spec))
  149. g.Expect(gotCes.Status).To(Equal(expectedCPS.Status))
  150. }).WithTimeout(timeout).WithPolling(interval).Should(Succeed())
  151. By("checking the push secrets")
  152. expectedPSs := tc.expectedPushSecrets(namespaces, pes)
  153. Eventually(func(g Gomega) {
  154. var gotESs []v1alpha1.PushSecret
  155. for _, ns := range namespaces {
  156. var pushSecrets v1alpha1.PushSecretList
  157. err := k8sClient.List(ctx, &pushSecrets, crclient.InNamespace(ns.Name))
  158. g.Expect(err).ShouldNot(HaveOccurred())
  159. gotESs = append(gotESs, pushSecrets.Items...)
  160. }
  161. g.Expect(len(gotESs)).Should(Equal(len(expectedPSs)))
  162. for _, gotES := range gotESs {
  163. found := false
  164. for _, expectedPS := range expectedPSs {
  165. if gotES.Namespace == expectedPS.Namespace && gotES.Name == expectedPS.Name {
  166. found = true
  167. g.Expect(gotES.Labels).To(Equal(expectedPS.Labels))
  168. g.Expect(gotES.Annotations).To(Equal(expectedPS.Annotations))
  169. g.Expect(gotES.Spec).To(Equal(expectedPS.Spec))
  170. }
  171. }
  172. g.Expect(found).To(Equal(true))
  173. }
  174. }).WithTimeout(timeout).WithPolling(interval).Should(Succeed())
  175. },
  176. Entry("Should use cluster push secret name if push secret name isn't defined", clusterPushSecretTestCase{
  177. namespaces: []v1.Namespace{
  178. {ObjectMeta: metav1.ObjectMeta{Name: randomNamespaceName()}},
  179. },
  180. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  181. pes := defaultClusterPushSecret()
  182. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  183. {
  184. MatchLabels: map[string]string{kubernetesMetadataLabel: namespaces[0].Name},
  185. },
  186. }
  187. return *pes
  188. },
  189. sourceSecret: defaultSourceSecret,
  190. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  191. return v1alpha1.ClusterPushSecret{
  192. ObjectMeta: metav1.ObjectMeta{
  193. Name: created.Name,
  194. },
  195. Spec: created.Spec,
  196. Status: v1alpha1.ClusterPushSecretStatus{
  197. PushSecretName: created.Name,
  198. ProvisionedNamespaces: []string{namespaces[0].Name},
  199. Conditions: []v1alpha1.PushSecretStatusCondition{
  200. {
  201. Type: v1alpha1.PushSecretReady,
  202. Status: v1.ConditionTrue,
  203. },
  204. },
  205. },
  206. }
  207. },
  208. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  209. return []v1alpha1.PushSecret{
  210. {
  211. ObjectMeta: metav1.ObjectMeta{
  212. Namespace: namespaces[0].Name,
  213. Name: created.Name,
  214. },
  215. Spec: created.Spec.PushSecretSpec,
  216. },
  217. }
  218. },
  219. }),
  220. Entry("Should set push secret name and metadata if the fields are set", clusterPushSecretTestCase{
  221. namespaces: []v1.Namespace{
  222. {ObjectMeta: metav1.ObjectMeta{Name: randomNamespaceName()}},
  223. },
  224. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  225. pes := defaultClusterPushSecret()
  226. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  227. {
  228. MatchLabels: map[string]string{kubernetesMetadataLabel: namespaces[0].Name},
  229. },
  230. }
  231. pes.Spec.PushSecretName = testPushSecret
  232. pes.Spec.PushSecretMetadata = v1alpha1.PushSecretMetadata{
  233. Labels: map[string]string{"test-label-key1": "test-label-value1", "test-label-key2": "test-label-value2"},
  234. Annotations: map[string]string{"test-annotation-key1": "test-annotation-value1", "test-annotation-key2": "test-annotation-value2"},
  235. }
  236. return *pes
  237. },
  238. sourceSecret: defaultSourceSecret,
  239. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  240. return v1alpha1.ClusterPushSecret{
  241. ObjectMeta: metav1.ObjectMeta{
  242. Name: created.Name,
  243. },
  244. Spec: created.Spec,
  245. Status: v1alpha1.ClusterPushSecretStatus{
  246. PushSecretName: testPushSecret,
  247. ProvisionedNamespaces: []string{namespaces[0].Name},
  248. Conditions: []v1alpha1.PushSecretStatusCondition{
  249. {
  250. Type: v1alpha1.PushSecretReady,
  251. Status: v1.ConditionTrue,
  252. },
  253. },
  254. },
  255. }
  256. },
  257. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  258. return []v1alpha1.PushSecret{
  259. {
  260. ObjectMeta: metav1.ObjectMeta{
  261. Namespace: namespaces[0].Name,
  262. Name: testPushSecret,
  263. Labels: map[string]string{"test-label-key1": "test-label-value1", "test-label-key2": "test-label-value2"},
  264. Annotations: map[string]string{"test-annotation-key1": "test-annotation-value1", "test-annotation-key2": "test-annotation-value2"},
  265. },
  266. Spec: created.Spec.PushSecretSpec,
  267. },
  268. }
  269. },
  270. }),
  271. Entry("Should delete old push secrets if name has changed", clusterPushSecretTestCase{
  272. namespaces: []v1.Namespace{
  273. {ObjectMeta: metav1.ObjectMeta{Name: randomNamespaceName()}},
  274. },
  275. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  276. pes := defaultClusterPushSecret()
  277. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  278. {
  279. MatchLabels: map[string]string{kubernetesMetadataLabel: namespaces[0].Name},
  280. },
  281. }
  282. pes.Spec.PushSecretName = "old-es-name"
  283. return *pes
  284. },
  285. sourceSecret: defaultSourceSecret,
  286. beforeCheck: func(ctx context.Context, namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) {
  287. // Wait until the push secret is provisioned
  288. var es v1alpha1.PushSecret
  289. Eventually(func(g Gomega) {
  290. key := types.NamespacedName{Namespace: namespaces[0].Name, Name: "old-es-name"}
  291. g.Expect(k8sClient.Get(ctx, key, &es)).ShouldNot(HaveOccurred())
  292. }).WithTimeout(timeout).WithPolling(interval).Should(Succeed())
  293. copied := created.DeepCopy()
  294. copied.Spec.PushSecretName = newPushSecret
  295. Expect(k8sClient.Patch(ctx, copied, crclient.MergeFrom(created.DeepCopy()))).ShouldNot(HaveOccurred())
  296. },
  297. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  298. updatedSpec := created.Spec.DeepCopy()
  299. updatedSpec.PushSecretName = newPushSecret
  300. return v1alpha1.ClusterPushSecret{
  301. ObjectMeta: metav1.ObjectMeta{
  302. Name: created.Name,
  303. },
  304. Spec: *updatedSpec,
  305. Status: v1alpha1.ClusterPushSecretStatus{
  306. PushSecretName: newPushSecret,
  307. ProvisionedNamespaces: []string{namespaces[0].Name},
  308. Conditions: []v1alpha1.PushSecretStatusCondition{
  309. {
  310. Type: v1alpha1.PushSecretReady,
  311. Status: v1.ConditionTrue,
  312. },
  313. },
  314. },
  315. }
  316. },
  317. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  318. return []v1alpha1.PushSecret{
  319. {
  320. ObjectMeta: metav1.ObjectMeta{
  321. Namespace: namespaces[0].Name,
  322. Name: newPushSecret,
  323. },
  324. Spec: created.Spec.PushSecretSpec,
  325. },
  326. }
  327. },
  328. }),
  329. Entry("Should update push secret if the fields change", clusterPushSecretTestCase{
  330. namespaces: []v1.Namespace{
  331. {ObjectMeta: metav1.ObjectMeta{Name: randomNamespaceName()}},
  332. },
  333. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  334. pes := defaultClusterPushSecret()
  335. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  336. {
  337. MatchLabels: map[string]string{kubernetesMetadataLabel: namespaces[0].Name},
  338. },
  339. }
  340. return *pes
  341. },
  342. sourceSecret: defaultSourceSecret,
  343. beforeCheck: func(ctx context.Context, namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) {
  344. // Wait until the push secret is provisioned
  345. var es v1alpha1.PushSecret
  346. Eventually(func(g Gomega) {
  347. key := types.NamespacedName{Namespace: namespaces[0].Name, Name: created.Name}
  348. g.Expect(k8sClient.Get(ctx, key, &es)).ShouldNot(HaveOccurred())
  349. g.Expect(len(es.Labels)).Should(Equal(0))
  350. g.Expect(len(es.Annotations)).Should(Equal(0))
  351. g.Expect(es.Spec).Should(Equal(created.Spec.PushSecretSpec))
  352. }).WithTimeout(timeout).WithPolling(interval).Should(Succeed())
  353. copied := created.DeepCopy()
  354. copied.Spec.PushSecretMetadata = v1alpha1.PushSecretMetadata{
  355. Labels: map[string]string{testLabelKey: testLabelValue},
  356. Annotations: map[string]string{testAnnotationKey: testAnnotationValue},
  357. }
  358. copied.Spec.PushSecretSpec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  359. {
  360. Name: updateStoreName,
  361. Kind: "SecretStore",
  362. },
  363. }
  364. Expect(k8sClient.Patch(ctx, copied, crclient.MergeFrom(created.DeepCopy()))).ShouldNot(HaveOccurred())
  365. },
  366. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  367. updatedSpec := created.Spec.DeepCopy()
  368. updatedSpec.PushSecretMetadata = v1alpha1.PushSecretMetadata{
  369. Labels: map[string]string{testLabelKey: testLabelValue},
  370. Annotations: map[string]string{testAnnotationKey: testAnnotationValue},
  371. }
  372. updatedSpec.PushSecretSpec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  373. {
  374. Name: updateStoreName,
  375. Kind: "SecretStore",
  376. },
  377. }
  378. return v1alpha1.ClusterPushSecret{
  379. ObjectMeta: metav1.ObjectMeta{
  380. Name: created.Name,
  381. },
  382. Spec: *updatedSpec,
  383. Status: v1alpha1.ClusterPushSecretStatus{
  384. PushSecretName: created.Name,
  385. ProvisionedNamespaces: []string{namespaces[0].Name},
  386. Conditions: []v1alpha1.PushSecretStatusCondition{
  387. {
  388. Type: v1alpha1.PushSecretReady,
  389. Status: v1.ConditionTrue,
  390. },
  391. },
  392. },
  393. }
  394. },
  395. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  396. updatedSpec := created.Spec.PushSecretSpec.DeepCopy()
  397. updatedSpec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  398. {
  399. Name: updateStoreName,
  400. Kind: "SecretStore",
  401. },
  402. }
  403. return []v1alpha1.PushSecret{
  404. {
  405. ObjectMeta: metav1.ObjectMeta{
  406. Namespace: namespaces[0].Name,
  407. Name: created.Name,
  408. Labels: map[string]string{testLabelKey: testLabelValue},
  409. Annotations: map[string]string{testAnnotationKey: testAnnotationValue},
  410. },
  411. Spec: *updatedSpec,
  412. },
  413. }
  414. },
  415. }),
  416. Entry("Should not overwrite existing push secrets and error out if one is present", clusterPushSecretTestCase{
  417. namespaces: []v1.Namespace{
  418. {ObjectMeta: metav1.ObjectMeta{Name: randomNamespaceName()}},
  419. },
  420. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  421. pes := defaultClusterPushSecret()
  422. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  423. {
  424. MatchLabels: map[string]string{kubernetesMetadataLabel: namespaces[0].Name},
  425. },
  426. }
  427. es := &v1alpha1.PushSecret{
  428. ObjectMeta: metav1.ObjectMeta{
  429. Name: pes.Name,
  430. Namespace: namespaces[0].Name,
  431. },
  432. Spec: v1alpha1.PushSecretSpec{
  433. RefreshInterval: &metav1.Duration{Duration: time.Hour},
  434. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  435. {
  436. Name: updateStoreName,
  437. },
  438. },
  439. Selector: v1alpha1.PushSecretSelector{
  440. Secret: &v1alpha1.PushSecretSecret{
  441. Name: secretName,
  442. },
  443. },
  444. },
  445. }
  446. Expect(k8sClient.Create(context.Background(), es)).ShouldNot(HaveOccurred())
  447. return *pes
  448. },
  449. sourceSecret: defaultSourceSecret,
  450. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  451. return v1alpha1.ClusterPushSecret{
  452. ObjectMeta: metav1.ObjectMeta{
  453. Name: created.Name,
  454. },
  455. Spec: created.Spec,
  456. Status: v1alpha1.ClusterPushSecretStatus{
  457. PushSecretName: created.Name,
  458. FailedNamespaces: []v1alpha1.ClusterPushSecretNamespaceFailure{
  459. {
  460. Namespace: namespaces[0].Name,
  461. Reason: "push secret already exists in namespace",
  462. },
  463. },
  464. Conditions: []v1alpha1.PushSecretStatusCondition{
  465. {
  466. Type: v1alpha1.PushSecretReady,
  467. Status: v1.ConditionFalse,
  468. Message: "one or more namespaces failed",
  469. },
  470. },
  471. },
  472. }
  473. },
  474. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  475. return []v1alpha1.PushSecret{
  476. {
  477. ObjectMeta: metav1.ObjectMeta{
  478. Namespace: namespaces[0].Name,
  479. Name: created.Name,
  480. },
  481. Spec: v1alpha1.PushSecretSpec{
  482. RefreshInterval: &metav1.Duration{Duration: time.Hour},
  483. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  484. {
  485. Name: updateStoreName,
  486. Kind: "SecretStore",
  487. },
  488. },
  489. UpdatePolicy: "Replace",
  490. Selector: v1alpha1.PushSecretSelector{
  491. Secret: &v1alpha1.PushSecretSecret{
  492. Name: secretName,
  493. },
  494. },
  495. DeletionPolicy: "None",
  496. },
  497. },
  498. }
  499. },
  500. }),
  501. Entry("Should crate an push secret if one with the same name has been deleted", clusterPushSecretTestCase{
  502. namespaces: []v1.Namespace{
  503. {ObjectMeta: metav1.ObjectMeta{Name: randomNamespaceName()}},
  504. },
  505. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  506. pes := defaultClusterPushSecret()
  507. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  508. {
  509. MatchLabels: map[string]string{kubernetesMetadataLabel: namespaces[0].Name},
  510. },
  511. }
  512. es := &v1alpha1.PushSecret{
  513. ObjectMeta: metav1.ObjectMeta{
  514. Name: pes.Name,
  515. Namespace: namespaces[0].Name,
  516. },
  517. Spec: v1alpha1.PushSecretSpec{
  518. RefreshInterval: &metav1.Duration{Duration: time.Hour},
  519. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  520. {
  521. Name: updateStoreName,
  522. },
  523. },
  524. Selector: v1alpha1.PushSecretSelector{
  525. Secret: &v1alpha1.PushSecretSecret{
  526. Name: secretName,
  527. },
  528. },
  529. },
  530. }
  531. Expect(k8sClient.Create(context.Background(), es)).ShouldNot(HaveOccurred())
  532. return *pes
  533. },
  534. sourceSecret: defaultSourceSecret,
  535. beforeCheck: func(ctx context.Context, namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) {
  536. pes := v1alpha1.ClusterPushSecret{}
  537. Eventually(func(g Gomega) {
  538. key := types.NamespacedName{Namespace: created.Namespace, Name: created.Name}
  539. g.Expect(k8sClient.Get(ctx, key, &pes)).ShouldNot(HaveOccurred())
  540. g.Expect(len(pes.Status.FailedNamespaces)).Should(Equal(1))
  541. }).WithTimeout(timeout).WithPolling(interval).Should(Succeed())
  542. es := &v1alpha1.PushSecret{
  543. ObjectMeta: metav1.ObjectMeta{
  544. Name: pes.Name,
  545. Namespace: namespaces[0].Name,
  546. },
  547. }
  548. Expect(k8sClient.Delete(ctx, es)).ShouldNot(HaveOccurred())
  549. },
  550. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  551. return v1alpha1.ClusterPushSecret{
  552. ObjectMeta: metav1.ObjectMeta{
  553. Name: created.Name,
  554. },
  555. Spec: created.Spec,
  556. Status: v1alpha1.ClusterPushSecretStatus{
  557. PushSecretName: created.Name,
  558. ProvisionedNamespaces: []string{namespaces[0].Name},
  559. Conditions: []v1alpha1.PushSecretStatusCondition{
  560. {
  561. Type: v1alpha1.PushSecretReady,
  562. Status: v1.ConditionTrue,
  563. },
  564. },
  565. },
  566. }
  567. },
  568. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  569. return []v1alpha1.PushSecret{
  570. {
  571. ObjectMeta: metav1.ObjectMeta{
  572. Namespace: namespaces[0].Name,
  573. Name: created.Name,
  574. },
  575. Spec: created.Spec.PushSecretSpec,
  576. },
  577. }
  578. },
  579. }),
  580. Entry("Should delete push secrets when namespaces no longer match", clusterPushSecretTestCase{
  581. namespaces: []v1.Namespace{
  582. {
  583. ObjectMeta: metav1.ObjectMeta{
  584. Name: randomNamespaceName(),
  585. Labels: map[string]string{noneMatchingAnnotationKey: noneMatchingAnnotationVal},
  586. },
  587. },
  588. {
  589. ObjectMeta: metav1.ObjectMeta{
  590. Name: randomNamespaceName(),
  591. Labels: map[string]string{noneMatchingAnnotationKey: noneMatchingAnnotationVal},
  592. },
  593. },
  594. },
  595. sourceSecret: defaultSourceSecret,
  596. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  597. pes := defaultClusterPushSecret()
  598. pes.Spec.RefreshInterval = &metav1.Duration{Duration: 100 * time.Millisecond}
  599. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  600. {
  601. MatchLabels: map[string]string{noneMatchingAnnotationKey: noneMatchingAnnotationVal},
  602. },
  603. }
  604. return *pes
  605. },
  606. beforeCheck: func(ctx context.Context, namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) {
  607. // Wait until the target ESs have been created
  608. Eventually(func(g Gomega) {
  609. for _, ns := range namespaces {
  610. key := types.NamespacedName{Namespace: ns.Name, Name: created.Name}
  611. g.Expect(k8sClient.Get(ctx, key, &v1alpha1.PushSecret{})).ShouldNot(HaveOccurred())
  612. }
  613. }).WithTimeout(timeout).WithPolling(interval).Should(Succeed())
  614. namespaces[0].Labels = map[string]string{}
  615. Expect(k8sClient.Update(ctx, &namespaces[0])).ShouldNot(HaveOccurred())
  616. },
  617. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  618. return v1alpha1.ClusterPushSecret{
  619. ObjectMeta: metav1.ObjectMeta{
  620. Name: created.Name,
  621. },
  622. Spec: created.Spec,
  623. Status: v1alpha1.ClusterPushSecretStatus{
  624. PushSecretName: created.Name,
  625. ProvisionedNamespaces: []string{namespaces[1].Name},
  626. Conditions: []v1alpha1.PushSecretStatusCondition{
  627. {
  628. Type: v1alpha1.PushSecretReady,
  629. Status: v1.ConditionTrue,
  630. },
  631. },
  632. },
  633. }
  634. },
  635. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  636. return []v1alpha1.PushSecret{
  637. {
  638. ObjectMeta: metav1.ObjectMeta{
  639. Namespace: namespaces[1].Name,
  640. Name: created.Name,
  641. },
  642. Spec: created.Spec.PushSecretSpec,
  643. },
  644. }
  645. },
  646. }),
  647. Entry("Should sync with match expression", clusterPushSecretTestCase{
  648. namespaces: []v1.Namespace{
  649. {
  650. ObjectMeta: metav1.ObjectMeta{
  651. Name: randomNamespaceName(),
  652. Labels: map[string]string{"prefix": "foo"},
  653. },
  654. },
  655. {
  656. ObjectMeta: metav1.ObjectMeta{
  657. Name: randomNamespaceName(),
  658. Labels: map[string]string{"prefix": "bar"},
  659. },
  660. },
  661. {
  662. ObjectMeta: metav1.ObjectMeta{
  663. Name: randomNamespaceName(),
  664. Labels: map[string]string{"prefix": "baz"},
  665. },
  666. },
  667. },
  668. sourceSecret: defaultSourceSecret,
  669. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  670. pes := defaultClusterPushSecret()
  671. pes.Spec.RefreshInterval = &metav1.Duration{Duration: 100 * time.Millisecond}
  672. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  673. {
  674. MatchExpressions: []metav1.LabelSelectorRequirement{
  675. {
  676. Key: "prefix",
  677. Operator: metav1.LabelSelectorOpIn,
  678. Values: []string{"foo", "bar"}, // "baz" is excluded
  679. },
  680. },
  681. },
  682. }
  683. return *pes
  684. },
  685. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  686. provisionedNamespaces := []string{namespaces[0].Name, namespaces[1].Name}
  687. sort.Strings(provisionedNamespaces)
  688. return v1alpha1.ClusterPushSecret{
  689. ObjectMeta: metav1.ObjectMeta{
  690. Name: created.Name,
  691. },
  692. Spec: created.Spec,
  693. Status: v1alpha1.ClusterPushSecretStatus{
  694. PushSecretName: created.Name,
  695. ProvisionedNamespaces: provisionedNamespaces,
  696. Conditions: []v1alpha1.PushSecretStatusCondition{
  697. {
  698. Type: v1alpha1.PushSecretReady,
  699. Status: v1.ConditionTrue,
  700. },
  701. },
  702. },
  703. }
  704. },
  705. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  706. return []v1alpha1.PushSecret{
  707. {
  708. ObjectMeta: metav1.ObjectMeta{
  709. Namespace: namespaces[0].Name,
  710. Name: created.Name,
  711. },
  712. Spec: created.Spec.PushSecretSpec,
  713. },
  714. {
  715. ObjectMeta: metav1.ObjectMeta{
  716. Namespace: namespaces[1].Name,
  717. Name: created.Name,
  718. },
  719. Spec: created.Spec.PushSecretSpec,
  720. },
  721. }
  722. },
  723. }),
  724. Entry("Should be ready if no namespace matches", clusterPushSecretTestCase{
  725. namespaces: []v1.Namespace{
  726. {
  727. ObjectMeta: metav1.ObjectMeta{
  728. Name: randomNamespaceName(),
  729. },
  730. },
  731. },
  732. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  733. pes := defaultClusterPushSecret()
  734. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  735. {
  736. MatchLabels: map[string]string{kubernetesMetadataLabel: "no-namespace-matches"},
  737. },
  738. }
  739. return *pes
  740. },
  741. sourceSecret: defaultSourceSecret,
  742. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  743. return v1alpha1.ClusterPushSecret{
  744. ObjectMeta: metav1.ObjectMeta{
  745. Name: created.Name,
  746. },
  747. Spec: created.Spec,
  748. Status: v1alpha1.ClusterPushSecretStatus{
  749. PushSecretName: created.Name,
  750. Conditions: []v1alpha1.PushSecretStatusCondition{
  751. {
  752. Type: v1alpha1.PushSecretReady,
  753. Status: v1.ConditionTrue,
  754. },
  755. },
  756. },
  757. }
  758. },
  759. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  760. return []v1alpha1.PushSecret{}
  761. },
  762. }),
  763. Entry("Should be ready if namespace is selected via the namespace selectors", clusterPushSecretTestCase{
  764. namespaces: []v1.Namespace{
  765. {
  766. ObjectMeta: metav1.ObjectMeta{
  767. Name: "namespace1",
  768. Labels: map[string]string{
  769. "key": "value1",
  770. },
  771. },
  772. },
  773. {
  774. ObjectMeta: metav1.ObjectMeta{
  775. Name: "namespace2",
  776. Labels: map[string]string{
  777. "key": "value2",
  778. },
  779. },
  780. },
  781. {
  782. ObjectMeta: metav1.ObjectMeta{
  783. Name: "namespace3",
  784. Labels: map[string]string{
  785. "key": "value3",
  786. },
  787. },
  788. },
  789. },
  790. clusterPushSecret: func(namespaces []v1.Namespace) v1alpha1.ClusterPushSecret {
  791. pes := defaultClusterPushSecret()
  792. pes.Spec.NamespaceSelectors = []*metav1.LabelSelector{
  793. {
  794. MatchLabels: map[string]string{"key": "value1"},
  795. },
  796. {
  797. MatchLabels: map[string]string{"key": "value2"},
  798. },
  799. }
  800. return *pes
  801. },
  802. sourceSecret: defaultSourceSecret,
  803. expectedClusterPushSecret: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) v1alpha1.ClusterPushSecret {
  804. return v1alpha1.ClusterPushSecret{
  805. ObjectMeta: metav1.ObjectMeta{
  806. Name: created.Name,
  807. },
  808. Spec: created.Spec,
  809. Status: v1alpha1.ClusterPushSecretStatus{
  810. PushSecretName: created.Name,
  811. ProvisionedNamespaces: []string{
  812. "namespace1",
  813. "namespace2",
  814. },
  815. Conditions: []v1alpha1.PushSecretStatusCondition{
  816. {
  817. Type: v1alpha1.PushSecretReady,
  818. Status: v1.ConditionTrue,
  819. },
  820. },
  821. },
  822. }
  823. },
  824. expectedPushSecrets: func(namespaces []v1.Namespace, created v1alpha1.ClusterPushSecret) []v1alpha1.PushSecret {
  825. return []v1alpha1.PushSecret{
  826. {
  827. ObjectMeta: metav1.ObjectMeta{
  828. Namespace: "namespace1",
  829. Name: created.Name,
  830. },
  831. Spec: created.Spec.PushSecretSpec,
  832. },
  833. {
  834. ObjectMeta: metav1.ObjectMeta{
  835. Namespace: "namespace2",
  836. Name: created.Name,
  837. },
  838. Spec: created.Spec.PushSecretSpec,
  839. },
  840. }
  841. },
  842. }))
  843. })
  844. var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz")
  845. func randString(n int) string {
  846. b := make([]rune, n)
  847. for i := range b {
  848. b[i] = letterRunes[rand.Intn(len(letterRunes))]
  849. }
  850. return string(b)
  851. }
  852. func randomNamespaceName() string {
  853. return fmt.Sprintf("testns-%s", randString(10))
  854. }