kubernetes_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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 kubernetes
  13. import (
  14. "context"
  15. "errors"
  16. "reflect"
  17. "testing"
  18. "github.com/stretchr/testify/assert"
  19. corev1 "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/utils/pointer"
  22. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  23. fclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
  24. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  25. v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
  26. )
  27. const (
  28. errSomethingWentWrong = "Something went wrong"
  29. testCertificate = `-----BEGIN CERTIFICATE-----
  30. MIIDHTCCAgWgAwIBAgIRAKC4yxy9QGocND+6avTf7BgwDQYJKoZIhvcNAQELBQAw
  31. EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMTAzMjAyMDA4MDhaFw0yMTAzMjAyMDM4
  32. MDhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
  33. ggEKAoIBAQC3o6/JdZEqNbqNRkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4Nzj
  34. aG15owr92/11W0pxPUliRLti3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvW
  35. Y8jh8A0LQALZiV/9QsrJdXZdS47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE
  36. 1gEDqnKfRxXI8DEQKXr+CKPUwCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4e
  37. ugHe52vXHdh/HJ9VjNp0xOH1waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJa
  38. YOOonQSEswveSv6PcG9AHvpNPot2Xs6hAgMBAAGjbjBsMA4GA1UdDwEB/wQEAwIC
  39. pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
  40. BBR00805mrpoonp95RmC3B6oLl+cGTAVBgNVHREEDjAMggpnb29ibGUuY29tMA0G
  41. CSqGSIb3DQEBCwUAA4IBAQAipc1b6JrEDayPjpz5GM5krcI8dCWVd8re0a9bGjjN
  42. ioWGlu/eTr5El0ffwCNZ2WLmL9rewfHf/bMvYz3ioFZJ2OTxfazqYXNggQz6cMfa
  43. lbedDCdt5XLVX2TyerGvFram+9Uyvk3l0uM7rZnwAmdirG4Tv94QRaD3q4xTj/c0
  44. mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
  45. 9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
  46. QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
  47. -----END CERTIFICATE-----`
  48. )
  49. type fakeClient struct {
  50. t *testing.T
  51. secretMap map[string]corev1.Secret
  52. expectedListOptions metav1.ListOptions
  53. }
  54. func (fk fakeClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*corev1.Secret, error) {
  55. secret, ok := fk.secretMap[name]
  56. if !ok {
  57. return nil, errors.New(errSomethingWentWrong)
  58. }
  59. return &secret, nil
  60. }
  61. func (fk fakeClient) List(ctx context.Context, opts metav1.ListOptions) (*corev1.SecretList, error) {
  62. assert.Equal(fk.t, fk.expectedListOptions, opts)
  63. list := &corev1.SecretList{}
  64. for _, v := range fk.secretMap {
  65. list.Items = append(list.Items, v)
  66. }
  67. return list, nil
  68. }
  69. func TestGetSecret(t *testing.T) {
  70. type fields struct {
  71. Client KClient
  72. ReviewClient RClient
  73. Namespace string
  74. }
  75. tests := []struct {
  76. name string
  77. fields fields
  78. ref esv1beta1.ExternalSecretDataRemoteRef
  79. want []byte
  80. wantErr bool
  81. }{
  82. {
  83. name: "err GetSecretMap",
  84. fields: fields{
  85. Client: fakeClient{
  86. t: t,
  87. secretMap: map[string]corev1.Secret{},
  88. },
  89. Namespace: "default",
  90. },
  91. ref: esv1beta1.ExternalSecretDataRemoteRef{
  92. Key: "mysec",
  93. Property: "token",
  94. },
  95. wantErr: true,
  96. },
  97. {
  98. name: "wrong property",
  99. fields: fields{
  100. Client: fakeClient{
  101. t: t,
  102. secretMap: map[string]corev1.Secret{
  103. "mysec": {
  104. Data: map[string][]byte{
  105. "token": []byte(`foobar`),
  106. },
  107. },
  108. },
  109. },
  110. Namespace: "default",
  111. },
  112. ref: esv1beta1.ExternalSecretDataRemoteRef{
  113. Key: "mysec",
  114. Property: "not-the-token",
  115. },
  116. wantErr: true,
  117. },
  118. {
  119. name: "successful case",
  120. fields: fields{
  121. Client: fakeClient{
  122. t: t,
  123. secretMap: map[string]corev1.Secret{
  124. "mysec": {
  125. Data: map[string][]byte{
  126. "token": []byte(`foobar`),
  127. },
  128. },
  129. },
  130. },
  131. Namespace: "default",
  132. },
  133. ref: esv1beta1.ExternalSecretDataRemoteRef{
  134. Key: "mysec",
  135. Property: "token",
  136. },
  137. want: []byte(`foobar`),
  138. },
  139. {
  140. name: "successful case without property",
  141. fields: fields{
  142. Client: fakeClient{
  143. t: t,
  144. secretMap: map[string]corev1.Secret{
  145. "mysec": {
  146. Data: map[string][]byte{
  147. "token": []byte(`foobar`),
  148. },
  149. },
  150. },
  151. },
  152. Namespace: "default",
  153. },
  154. ref: esv1beta1.ExternalSecretDataRemoteRef{
  155. Key: "mysec",
  156. },
  157. want: []byte(`{"token":"foobar"}`),
  158. },
  159. }
  160. for _, tt := range tests {
  161. t.Run(tt.name, func(t *testing.T) {
  162. p := &ProviderKubernetes{
  163. Client: tt.fields.Client,
  164. ReviewClient: tt.fields.ReviewClient,
  165. Namespace: tt.fields.Namespace,
  166. }
  167. got, err := p.GetSecret(context.Background(), tt.ref)
  168. if (err != nil) != tt.wantErr {
  169. t.Errorf("ProviderKubernetes.GetSecret() error = %v, wantErr %v", err, tt.wantErr)
  170. return
  171. }
  172. if !reflect.DeepEqual(got, tt.want) {
  173. t.Errorf("ProviderKubernetes.GetSecret() = %v, want %v", got, tt.want)
  174. }
  175. })
  176. }
  177. }
  178. func TestNewClient(t *testing.T) {
  179. type fields struct {
  180. Client KClient
  181. ReviewClient RClient
  182. Namespace string
  183. }
  184. type args struct {
  185. store esv1beta1.GenericStore
  186. kube kclient.Client
  187. namespace string
  188. }
  189. tests := []struct {
  190. name string
  191. fields fields
  192. args args
  193. want bool
  194. wantErr bool
  195. }{
  196. {
  197. name: "invalid store",
  198. fields: fields{},
  199. args: args{
  200. store: &esv1beta1.ClusterSecretStore{
  201. TypeMeta: metav1.TypeMeta{
  202. Kind: esv1beta1.ClusterSecretStoreKind,
  203. },
  204. Spec: esv1beta1.SecretStoreSpec{
  205. Provider: &esv1beta1.SecretStoreProvider{},
  206. },
  207. },
  208. kube: fclient.NewClientBuilder().Build(),
  209. },
  210. wantErr: true,
  211. },
  212. {
  213. name: "test referent auth return",
  214. fields: fields{},
  215. args: args{
  216. store: &esv1beta1.ClusterSecretStore{
  217. TypeMeta: metav1.TypeMeta{
  218. Kind: esv1beta1.ClusterSecretStoreKind,
  219. },
  220. Spec: esv1beta1.SecretStoreSpec{
  221. Provider: &esv1beta1.SecretStoreProvider{
  222. Kubernetes: &esv1beta1.KubernetesProvider{
  223. Server: esv1beta1.KubernetesServer{
  224. CABundle: []byte(testCertificate),
  225. },
  226. Auth: esv1beta1.KubernetesAuth{
  227. Token: &esv1beta1.TokenAuth{
  228. BearerToken: v1.SecretKeySelector{
  229. Name: "foo",
  230. Key: "token",
  231. },
  232. },
  233. },
  234. },
  235. },
  236. },
  237. },
  238. namespace: "",
  239. kube: fclient.NewClientBuilder().Build(),
  240. },
  241. want: true,
  242. },
  243. {
  244. name: "auth fail results in error",
  245. fields: fields{},
  246. args: args{
  247. store: &esv1beta1.ClusterSecretStore{
  248. TypeMeta: metav1.TypeMeta{
  249. Kind: esv1beta1.ClusterSecretStoreKind,
  250. },
  251. Spec: esv1beta1.SecretStoreSpec{
  252. Provider: &esv1beta1.SecretStoreProvider{
  253. Kubernetes: &esv1beta1.KubernetesProvider{
  254. Server: esv1beta1.KubernetesServer{
  255. CABundle: []byte(testCertificate),
  256. },
  257. RemoteNamespace: "remote",
  258. Auth: esv1beta1.KubernetesAuth{
  259. Token: &esv1beta1.TokenAuth{
  260. BearerToken: v1.SecretKeySelector{
  261. Name: "foo",
  262. Namespace: pointer.String("default"),
  263. Key: "token",
  264. },
  265. },
  266. },
  267. },
  268. },
  269. },
  270. },
  271. namespace: "foobarothernamespace",
  272. kube: fclient.NewClientBuilder().Build(),
  273. },
  274. wantErr: true,
  275. },
  276. {
  277. name: "test auth",
  278. fields: fields{},
  279. args: args{
  280. store: &esv1beta1.ClusterSecretStore{
  281. TypeMeta: metav1.TypeMeta{
  282. Kind: esv1beta1.ClusterSecretStoreKind,
  283. },
  284. Spec: esv1beta1.SecretStoreSpec{
  285. Provider: &esv1beta1.SecretStoreProvider{
  286. Kubernetes: &esv1beta1.KubernetesProvider{
  287. Server: esv1beta1.KubernetesServer{
  288. CABundle: []byte(testCertificate),
  289. },
  290. RemoteNamespace: "remote",
  291. Auth: esv1beta1.KubernetesAuth{
  292. Token: &esv1beta1.TokenAuth{
  293. BearerToken: v1.SecretKeySelector{
  294. Name: "foo",
  295. Namespace: pointer.String("default"),
  296. Key: "token",
  297. },
  298. },
  299. },
  300. },
  301. },
  302. },
  303. },
  304. namespace: "foobarothernamespace",
  305. kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
  306. ObjectMeta: metav1.ObjectMeta{
  307. Name: "foo",
  308. Namespace: "default",
  309. },
  310. Data: map[string][]byte{
  311. "token": []byte("1234"),
  312. },
  313. }).Build(),
  314. },
  315. want: true,
  316. },
  317. }
  318. for _, tt := range tests {
  319. t.Run(tt.name, func(t *testing.T) {
  320. p := &ProviderKubernetes{
  321. Client: tt.fields.Client,
  322. ReviewClient: tt.fields.ReviewClient,
  323. Namespace: tt.fields.Namespace,
  324. }
  325. got, err := p.NewClient(context.Background(), tt.args.store, tt.args.kube, tt.args.namespace)
  326. if (err != nil) != tt.wantErr {
  327. t.Errorf("ProviderKubernetes.NewClient() error = %v, wantErr %v", err, tt.wantErr)
  328. return
  329. }
  330. if tt.want {
  331. assert.NotNil(t, got)
  332. } else {
  333. assert.Nil(t, got)
  334. }
  335. })
  336. }
  337. }
  338. func TestGetAllSecrets(t *testing.T) {
  339. type fields struct {
  340. Client KClient
  341. ReviewClient RClient
  342. Namespace string
  343. }
  344. type args struct {
  345. ctx context.Context
  346. ref esv1beta1.ExternalSecretFind
  347. }
  348. tests := []struct {
  349. name string
  350. fields fields
  351. args args
  352. want map[string][]byte
  353. wantErr bool
  354. }{
  355. {
  356. name: "use regex",
  357. fields: fields{
  358. Client: fakeClient{
  359. t: t,
  360. secretMap: map[string]corev1.Secret{
  361. "mysec": {
  362. ObjectMeta: metav1.ObjectMeta{
  363. Name: "mysec",
  364. },
  365. Data: map[string][]byte{
  366. "token": []byte(`foo`),
  367. },
  368. },
  369. "other": {
  370. ObjectMeta: metav1.ObjectMeta{
  371. Name: "other",
  372. },
  373. Data: map[string][]byte{
  374. "token": []byte(`bar`),
  375. },
  376. },
  377. },
  378. },
  379. },
  380. args: args{
  381. ref: esv1beta1.ExternalSecretFind{
  382. Name: &esv1beta1.FindName{
  383. RegExp: "other",
  384. },
  385. },
  386. },
  387. want: map[string][]byte{
  388. "other": []byte(`{"token":"bar"}`),
  389. },
  390. },
  391. {
  392. name: "use tags/labels",
  393. fields: fields{
  394. Client: fakeClient{
  395. t: t,
  396. expectedListOptions: metav1.ListOptions{
  397. LabelSelector: "app=foobar",
  398. },
  399. secretMap: map[string]corev1.Secret{
  400. "mysec": {
  401. ObjectMeta: metav1.ObjectMeta{
  402. Name: "mysec",
  403. },
  404. Data: map[string][]byte{
  405. "token": []byte(`foo`),
  406. },
  407. },
  408. "other": {
  409. ObjectMeta: metav1.ObjectMeta{
  410. Name: "other",
  411. },
  412. Data: map[string][]byte{
  413. "token": []byte(`bar`),
  414. },
  415. },
  416. },
  417. },
  418. },
  419. args: args{
  420. ref: esv1beta1.ExternalSecretFind{
  421. Tags: map[string]string{
  422. "app": "foobar",
  423. },
  424. },
  425. },
  426. want: map[string][]byte{
  427. "mysec": []byte(`{"token":"foo"}`),
  428. "other": []byte(`{"token":"bar"}`),
  429. },
  430. },
  431. }
  432. for _, tt := range tests {
  433. t.Run(tt.name, func(t *testing.T) {
  434. p := &ProviderKubernetes{
  435. Client: tt.fields.Client,
  436. ReviewClient: tt.fields.ReviewClient,
  437. Namespace: tt.fields.Namespace,
  438. }
  439. got, err := p.GetAllSecrets(tt.args.ctx, tt.args.ref)
  440. if (err != nil) != tt.wantErr {
  441. t.Errorf("ProviderKubernetes.GetAllSecrets() error = %v, wantErr %v", err, tt.wantErr)
  442. return
  443. }
  444. if !reflect.DeepEqual(got, tt.want) {
  445. t.Errorf("ProviderKubernetes.GetAllSecrets() = %v, want %v", got, tt.want)
  446. }
  447. })
  448. }
  449. }