server_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  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 store
  14. import (
  15. "context"
  16. "errors"
  17. "testing"
  18. corev1 "k8s.io/api/core/v1"
  19. "k8s.io/apimachinery/pkg/runtime/schema"
  20. "sigs.k8s.io/controller-runtime/pkg/client"
  21. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  22. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  23. pb "github.com/external-secrets/external-secrets/proto/provider"
  24. )
  25. const (
  26. serverTestRemoteKey = "remote/path"
  27. serverTestProperty = "property"
  28. serverTestSourceNamespace = "tenant-a"
  29. serverTestValue = "value"
  30. )
  31. type fakeProviderInterface struct {
  32. newClient func(context.Context, esv1.GenericStore, client.Client, string) (esv1.SecretsClient, error)
  33. caps esv1.SecretStoreCapabilities
  34. }
  35. func (f *fakeProviderInterface) NewClient(ctx context.Context, store esv1.GenericStore, kube client.Client, namespace string) (esv1.SecretsClient, error) {
  36. return f.newClient(ctx, store, kube, namespace)
  37. }
  38. func (f *fakeProviderInterface) Capabilities() esv1.SecretStoreCapabilities {
  39. return f.caps
  40. }
  41. func (f *fakeProviderInterface) ValidateStore(esv1.GenericStore) (admission.Warnings, error) {
  42. return nil, nil
  43. }
  44. type fakeSecretsClient struct {
  45. getSecretResponse []byte
  46. getSecretErr error
  47. getSecretRef esv1.ExternalSecretDataRemoteRef
  48. getSecretMapResponse map[string][]byte
  49. getSecretMapErr error
  50. getSecretMapRef esv1.ExternalSecretDataRemoteRef
  51. getAllSecretsResponse map[string][]byte
  52. getAllSecretsErr error
  53. getAllSecretsFind esv1.ExternalSecretFind
  54. pushSecretErr error
  55. pushSecretSecret *corev1.Secret
  56. pushSecretData esv1.PushSecretData
  57. deleteSecretErr error
  58. deleteSecretRef esv1.PushSecretRemoteRef
  59. secretExistsResp bool
  60. secretExistsErr error
  61. secretExistsRef esv1.PushSecretRemoteRef
  62. validateResult esv1.ValidationResult
  63. validateErr error
  64. closeCalled bool
  65. }
  66. func (f *fakeSecretsClient) GetSecret(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  67. f.getSecretRef = ref
  68. return f.getSecretResponse, f.getSecretErr
  69. }
  70. func (f *fakeSecretsClient) GetSecretMap(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  71. f.getSecretMapRef = ref
  72. return f.getSecretMapResponse, f.getSecretMapErr
  73. }
  74. func (f *fakeSecretsClient) GetAllSecrets(_ context.Context, find esv1.ExternalSecretFind) (map[string][]byte, error) {
  75. f.getAllSecretsFind = find
  76. return f.getAllSecretsResponse, f.getAllSecretsErr
  77. }
  78. func (f *fakeSecretsClient) PushSecret(_ context.Context, secret *corev1.Secret, data esv1.PushSecretData) error {
  79. f.pushSecretSecret = secret
  80. f.pushSecretData = data
  81. return f.pushSecretErr
  82. }
  83. func (f *fakeSecretsClient) DeleteSecret(_ context.Context, remoteRef esv1.PushSecretRemoteRef) error {
  84. f.deleteSecretRef = remoteRef
  85. return f.deleteSecretErr
  86. }
  87. func (f *fakeSecretsClient) SecretExists(_ context.Context, remoteRef esv1.PushSecretRemoteRef) (bool, error) {
  88. f.secretExistsRef = remoteRef
  89. return f.secretExistsResp, f.secretExistsErr
  90. }
  91. func (f *fakeSecretsClient) Validate() (esv1.ValidationResult, error) {
  92. return f.validateResult, f.validateErr
  93. }
  94. func (f *fakeSecretsClient) Close(context.Context) error {
  95. f.closeCalled = true
  96. return nil
  97. }
  98. type specMapperRecorder struct {
  99. ref *pb.ProviderReference
  100. sourceNamespace string
  101. spec *esv1.SecretStoreSpec
  102. err error
  103. }
  104. func (r *specMapperRecorder) mapRef(ref *pb.ProviderReference, sourceNamespace string) (*esv1.SecretStoreSpec, error) {
  105. r.ref = ref
  106. r.sourceNamespace = sourceNamespace
  107. return r.spec, r.err
  108. }
  109. func TestServerGetSecretMapsRemoteRefAndSyntheticStoreNamespace(t *testing.T) {
  110. mapper := &specMapperRecorder{
  111. spec: &esv1.SecretStoreSpec{
  112. Provider: &esv1.SecretStoreProvider{
  113. Fake: &esv1.FakeProvider{},
  114. },
  115. },
  116. }
  117. fakeClient := &fakeSecretsClient{getSecretResponse: []byte("secret-value")}
  118. var receivedStore esv1.GenericStore
  119. var receivedNamespace string
  120. server := NewServer(nil, ProviderMapping{
  121. schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
  122. caps: esv1.SecretStoreReadWrite,
  123. newClient: func(_ context.Context, store esv1.GenericStore, _ client.Client, namespace string) (esv1.SecretsClient, error) {
  124. receivedStore = store
  125. receivedNamespace = namespace
  126. return fakeClient, nil
  127. },
  128. },
  129. }, mapper.mapRef)
  130. req := &pb.GetSecretRequest{
  131. ProviderRef: &pb.ProviderReference{
  132. ApiVersion: "provider.external-secrets.io/v2alpha1",
  133. Kind: "Fake",
  134. Name: "backend",
  135. Namespace: "provider-config-ns",
  136. StoreRefKind: esv1.ProviderKindStr,
  137. },
  138. SourceNamespace: serverTestSourceNamespace,
  139. RemoteRef: &pb.ExternalSecretDataRemoteRef{
  140. Key: "sample",
  141. Version: "v1",
  142. Property: "password",
  143. DecodingStrategy: string(esv1.ExternalSecretDecodeBase64),
  144. MetadataPolicy: string(esv1.ExternalSecretMetadataPolicyFetch),
  145. },
  146. }
  147. resp, err := server.GetSecret(context.Background(), req)
  148. if err != nil {
  149. t.Fatalf("GetSecret() error = %v", err)
  150. }
  151. if string(resp.Value) != "secret-value" {
  152. t.Fatalf("expected secret-value, got %q", string(resp.Value))
  153. }
  154. if mapper.ref != req.ProviderRef || mapper.sourceNamespace != serverTestSourceNamespace {
  155. t.Fatalf("unexpected spec mapper input: ref=%#v namespace=%q", mapper.ref, mapper.sourceNamespace)
  156. }
  157. if receivedNamespace != serverTestSourceNamespace {
  158. t.Fatalf("unexpected new client namespace: %q", receivedNamespace)
  159. }
  160. syntheticStore, ok := receivedStore.(*SyntheticStore)
  161. if !ok {
  162. t.Fatalf("expected SyntheticStore, got %T", receivedStore)
  163. }
  164. if syntheticStore.Namespace != serverTestSourceNamespace {
  165. t.Fatalf("unexpected synthetic store namespace: %q", syntheticStore.Namespace)
  166. }
  167. if syntheticStore.Kind != esv1.SecretStoreKind {
  168. t.Fatalf("unexpected synthetic store kind: %q", syntheticStore.Kind)
  169. }
  170. if syntheticStore.GetSpec() != mapper.spec {
  171. t.Fatalf("unexpected synthetic spec: %#v", syntheticStore.GetSpec())
  172. }
  173. if fakeClient.getSecretRef.Key != "sample" || fakeClient.getSecretRef.Version != "v1" || fakeClient.getSecretRef.Property != "password" {
  174. t.Fatalf("unexpected remote ref: %#v", fakeClient.getSecretRef)
  175. }
  176. if fakeClient.getSecretRef.DecodingStrategy != esv1.ExternalSecretDecodeBase64 {
  177. t.Fatalf("unexpected decoding strategy: %q", fakeClient.getSecretRef.DecodingStrategy)
  178. }
  179. if fakeClient.getSecretRef.MetadataPolicy != esv1.ExternalSecretMetadataPolicyFetch {
  180. t.Fatalf("unexpected metadata policy: %q", fakeClient.getSecretRef.MetadataPolicy)
  181. }
  182. if !fakeClient.closeCalled {
  183. t.Fatal("expected secrets client to be closed")
  184. }
  185. }
  186. func TestServerPushSecretMapsClusterProviderStoreKindToClusterSecretStore(t *testing.T) {
  187. mapper := &specMapperRecorder{
  188. spec: &esv1.SecretStoreSpec{
  189. Provider: &esv1.SecretStoreProvider{
  190. Fake: &esv1.FakeProvider{},
  191. },
  192. },
  193. }
  194. fakeClient := &fakeSecretsClient{}
  195. var receivedStore esv1.GenericStore
  196. server := NewServer(nil, ProviderMapping{
  197. schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
  198. caps: esv1.SecretStoreReadWrite,
  199. newClient: func(_ context.Context, store esv1.GenericStore, _ client.Client, _ string) (esv1.SecretsClient, error) {
  200. receivedStore = store
  201. return fakeClient, nil
  202. },
  203. },
  204. }, mapper.mapRef)
  205. _, err := server.PushSecret(context.Background(), &pb.PushSecretRequest{
  206. ProviderRef: &pb.ProviderReference{
  207. ApiVersion: "provider.external-secrets.io/v2alpha1",
  208. Kind: "Fake",
  209. Name: "backend",
  210. StoreRefKind: esv1.ClusterProviderKindStr,
  211. },
  212. SourceNamespace: serverTestSourceNamespace,
  213. SecretData: map[string][]byte{
  214. serverTestValue: []byte("secret-value"),
  215. },
  216. PushSecretData: &pb.PushSecretData{
  217. RemoteKey: "remote-secret",
  218. SecretKey: serverTestValue,
  219. },
  220. })
  221. if err != nil {
  222. t.Fatalf("PushSecret() error = %v", err)
  223. }
  224. syntheticStore, ok := receivedStore.(*SyntheticStore)
  225. if !ok {
  226. t.Fatalf("expected SyntheticStore, got %T", receivedStore)
  227. }
  228. if syntheticStore.Kind != esv1.ClusterSecretStoreKind {
  229. t.Fatalf("unexpected synthetic store kind: %q", syntheticStore.Kind)
  230. }
  231. }
  232. func TestServerGetSecretMapDelegates(t *testing.T) {
  233. mapper := &specMapperRecorder{
  234. spec: &esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{Fake: &esv1.FakeProvider{}}},
  235. }
  236. fakeClient := &fakeSecretsClient{
  237. getSecretMapResponse: map[string][]byte{"foo": []byte("bar")},
  238. }
  239. server := NewServer(nil, ProviderMapping{
  240. schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
  241. caps: esv1.SecretStoreReadWrite,
  242. newClient: func(context.Context, esv1.GenericStore, client.Client, string) (esv1.SecretsClient, error) {
  243. return fakeClient, nil
  244. },
  245. },
  246. }, mapper.mapRef)
  247. resp, err := server.GetSecretMap(context.Background(), &pb.GetSecretMapRequest{
  248. ProviderRef: &pb.ProviderReference{
  249. ApiVersion: "provider.external-secrets.io/v2alpha1",
  250. Kind: "Fake",
  251. Name: "backend",
  252. },
  253. SourceNamespace: serverTestSourceNamespace,
  254. RemoteRef: &pb.ExternalSecretDataRemoteRef{Key: "sample"},
  255. })
  256. if err != nil {
  257. t.Fatalf("GetSecretMap() error = %v", err)
  258. }
  259. if string(resp.Secrets["foo"]) != "bar" {
  260. t.Fatalf("unexpected response: %#v", resp.Secrets)
  261. }
  262. if fakeClient.getSecretMapRef.Key != "sample" {
  263. t.Fatalf("unexpected ref: %#v", fakeClient.getSecretMapRef)
  264. }
  265. }
  266. func TestServerGetAllSecretsMapsFindCriteria(t *testing.T) {
  267. mapper := &specMapperRecorder{
  268. spec: &esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{Fake: &esv1.FakeProvider{}}},
  269. }
  270. fakeClient := &fakeSecretsClient{
  271. getAllSecretsResponse: map[string][]byte{"db-password": []byte(serverTestValue)},
  272. }
  273. server := NewServer(nil, ProviderMapping{
  274. schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
  275. caps: esv1.SecretStoreReadWrite,
  276. newClient: func(context.Context, esv1.GenericStore, client.Client, string) (esv1.SecretsClient, error) {
  277. return fakeClient, nil
  278. },
  279. },
  280. }, mapper.mapRef)
  281. resp, err := server.GetAllSecrets(context.Background(), &pb.GetAllSecretsRequest{
  282. ProviderRef: &pb.ProviderReference{
  283. ApiVersion: "provider.external-secrets.io/v2alpha1",
  284. Kind: "Fake",
  285. Name: "backend",
  286. },
  287. SourceNamespace: serverTestSourceNamespace,
  288. Find: &pb.ExternalSecretFind{
  289. Tags: map[string]string{"team": "a"},
  290. Path: "/team-a",
  291. ConversionStrategy: string(esv1.ExternalSecretConversionDefault),
  292. DecodingStrategy: string(esv1.ExternalSecretDecodeBase64),
  293. Name: &pb.FindName{Regexp: "db-.*"},
  294. },
  295. })
  296. if err != nil {
  297. t.Fatalf("GetAllSecrets() error = %v", err)
  298. }
  299. if string(resp.Secrets["db-password"]) != serverTestValue {
  300. t.Fatalf("unexpected response: %#v", resp.Secrets)
  301. }
  302. if fakeClient.getAllSecretsFind.Tags["team"] != "a" {
  303. t.Fatalf("unexpected find tags: %#v", fakeClient.getAllSecretsFind)
  304. }
  305. if fakeClient.getAllSecretsFind.Path == nil || *fakeClient.getAllSecretsFind.Path != "/team-a" {
  306. t.Fatalf("unexpected find path: %#v", fakeClient.getAllSecretsFind.Path)
  307. }
  308. if fakeClient.getAllSecretsFind.Name == nil || fakeClient.getAllSecretsFind.Name.RegExp != "db-.*" {
  309. t.Fatalf("unexpected find name: %#v", fakeClient.getAllSecretsFind.Name)
  310. }
  311. }
  312. func TestServerPushDeleteAndExistsMapWriteRequests(t *testing.T) {
  313. mapper := &specMapperRecorder{
  314. spec: &esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{Fake: &esv1.FakeProvider{}}},
  315. }
  316. fakeClient := &fakeSecretsClient{secretExistsResp: true}
  317. server := NewServer(nil, ProviderMapping{
  318. schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
  319. caps: esv1.SecretStoreReadWrite,
  320. newClient: func(context.Context, esv1.GenericStore, client.Client, string) (esv1.SecretsClient, error) {
  321. return fakeClient, nil
  322. },
  323. },
  324. }, mapper.mapRef)
  325. _, err := server.PushSecret(context.Background(), &pb.PushSecretRequest{
  326. ProviderRef: &pb.ProviderReference{
  327. ApiVersion: "provider.external-secrets.io/v2alpha1",
  328. Kind: "Fake",
  329. Name: "backend",
  330. },
  331. SourceNamespace: serverTestSourceNamespace,
  332. SecretData: map[string][]byte{
  333. "token": []byte(serverTestValue),
  334. },
  335. PushSecretData: &pb.PushSecretData{
  336. RemoteKey: serverTestRemoteKey,
  337. SecretKey: "token",
  338. Property: serverTestProperty,
  339. Metadata: []byte(`{"owner":"eso"}`),
  340. },
  341. })
  342. if err != nil {
  343. t.Fatalf("PushSecret() error = %v", err)
  344. }
  345. if fakeClient.pushSecretSecret == nil || string(fakeClient.pushSecretSecret.Data["token"]) != serverTestValue {
  346. t.Fatalf("unexpected pushed secret: %#v", fakeClient.pushSecretSecret)
  347. }
  348. if fakeClient.pushSecretSecret.Type != "" {
  349. t.Fatalf("unexpected secret type: %q", fakeClient.pushSecretSecret.Type)
  350. }
  351. if fakeClient.pushSecretData.GetRemoteKey() != serverTestRemoteKey || fakeClient.pushSecretData.GetSecretKey() != "token" || fakeClient.pushSecretData.GetProperty() != serverTestProperty {
  352. t.Fatalf("unexpected push data: %#v", fakeClient.pushSecretData)
  353. }
  354. if got := fakeClient.pushSecretData.GetMetadata(); got == nil || string(got.Raw) != `{"owner":"eso"}` {
  355. t.Fatalf("unexpected metadata: %#v", got)
  356. }
  357. _, err = server.DeleteSecret(context.Background(), &pb.DeleteSecretRequest{
  358. ProviderRef: &pb.ProviderReference{
  359. ApiVersion: "provider.external-secrets.io/v2alpha1",
  360. Kind: "Fake",
  361. Name: "backend",
  362. },
  363. SourceNamespace: serverTestSourceNamespace,
  364. RemoteRef: &pb.PushSecretRemoteRef{
  365. RemoteKey: serverTestRemoteKey,
  366. Property: serverTestProperty,
  367. },
  368. })
  369. if err != nil {
  370. t.Fatalf("DeleteSecret() error = %v", err)
  371. }
  372. if fakeClient.deleteSecretRef.GetRemoteKey() != serverTestRemoteKey || fakeClient.deleteSecretRef.GetProperty() != serverTestProperty {
  373. t.Fatalf("unexpected delete ref: %#v", fakeClient.deleteSecretRef)
  374. }
  375. resp, err := server.SecretExists(context.Background(), &pb.SecretExistsRequest{
  376. ProviderRef: &pb.ProviderReference{
  377. ApiVersion: "provider.external-secrets.io/v2alpha1",
  378. Kind: "Fake",
  379. Name: "backend",
  380. },
  381. SourceNamespace: serverTestSourceNamespace,
  382. RemoteRef: &pb.PushSecretRemoteRef{
  383. RemoteKey: serverTestRemoteKey,
  384. Property: serverTestProperty,
  385. },
  386. })
  387. if err != nil {
  388. t.Fatalf("SecretExists() error = %v", err)
  389. }
  390. if !resp.Exists {
  391. t.Fatal("expected exists response to be true")
  392. }
  393. if fakeClient.secretExistsRef.GetRemoteKey() != serverTestRemoteKey || fakeClient.secretExistsRef.GetProperty() != serverTestProperty {
  394. t.Fatalf("unexpected exists ref: %#v", fakeClient.secretExistsRef)
  395. }
  396. }
  397. func TestServerPushSecretForwardsKubernetesSecretMetadata(t *testing.T) {
  398. mapper := &specMapperRecorder{
  399. spec: &esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{Fake: &esv1.FakeProvider{}}},
  400. }
  401. fakeClient := &fakeSecretsClient{}
  402. server := NewServer(nil, ProviderMapping{
  403. schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
  404. caps: esv1.SecretStoreReadWrite,
  405. newClient: func(context.Context, esv1.GenericStore, client.Client, string) (esv1.SecretsClient, error) {
  406. return fakeClient, nil
  407. },
  408. },
  409. }, mapper.mapRef)
  410. req := &pb.PushSecretRequest{
  411. ProviderRef: &pb.ProviderReference{
  412. ApiVersion: "provider.external-secrets.io/v2alpha1",
  413. Kind: "Fake",
  414. Name: "backend",
  415. },
  416. SourceNamespace: serverTestSourceNamespace,
  417. SecretData: map[string][]byte{
  418. ".dockerconfigjson": []byte("payload"),
  419. },
  420. SecretType: string(corev1.SecretTypeDockerConfigJson),
  421. SecretLabels: map[string]string{"team": "platform"},
  422. SecretAnnotations: map[string]string{"owner": "app-team"},
  423. PushSecretData: &pb.PushSecretData{
  424. RemoteKey: serverTestRemoteKey,
  425. SecretKey: ".dockerconfigjson",
  426. Property: serverTestProperty,
  427. Metadata: []byte(`{"mergePolicy":"replace"}`),
  428. },
  429. }
  430. _, err := server.PushSecret(context.Background(), req)
  431. if err != nil {
  432. t.Fatalf("PushSecret() error = %v", err)
  433. }
  434. if fakeClient.pushSecretSecret == nil {
  435. t.Fatal("expected pushed secret to be recorded")
  436. }
  437. if got, want := string(fakeClient.pushSecretSecret.Data[".dockerconfigjson"]), "payload"; got != want {
  438. t.Errorf("expected payload %q, got %q", want, got)
  439. }
  440. if got, want := fakeClient.pushSecretSecret.Type, corev1.SecretTypeDockerConfigJson; got != want {
  441. t.Errorf("expected secret type %q, got %q", want, got)
  442. }
  443. if got, want := fakeClient.pushSecretSecret.Labels["team"], "platform"; got != want {
  444. t.Errorf("expected secret label team=%q, got %q", want, got)
  445. }
  446. if got, want := fakeClient.pushSecretSecret.Annotations["owner"], "app-team"; got != want {
  447. t.Errorf("expected secret annotation owner=%q, got %q", want, got)
  448. }
  449. }
  450. func TestServerValidateMapsReadyUnknownAndErrorResults(t *testing.T) {
  451. t.Run("ready", func(t *testing.T) {
  452. resp := runValidateTest(t, esv1.ValidationResultReady, nil)
  453. if !resp.Valid {
  454. t.Fatalf("expected valid response, got %#v", resp)
  455. }
  456. })
  457. t.Run("unknown", func(t *testing.T) {
  458. resp := runValidateTest(t, esv1.ValidationResultUnknown, nil)
  459. if !resp.Valid {
  460. t.Fatalf("expected unknown to be treated as valid, got %#v", resp)
  461. }
  462. })
  463. t.Run("error_result", func(t *testing.T) {
  464. resp := runValidateTest(t, esv1.ValidationResultError, nil)
  465. if resp.Valid {
  466. t.Fatalf("expected invalid response, got %#v", resp)
  467. }
  468. })
  469. t.Run("error", func(t *testing.T) {
  470. validateErr := errors.New("invalid credentials")
  471. resp := runValidateTest(t, esv1.ValidationResultError, validateErr)
  472. if resp.Valid || resp.Error != "invalid credentials" {
  473. t.Fatalf("unexpected response: %#v", resp)
  474. }
  475. })
  476. }
  477. func TestServerCapabilitiesMapsProviderCapabilities(t *testing.T) {
  478. testCases := []struct {
  479. name string
  480. caps esv1.SecretStoreCapabilities
  481. expectedPB pb.SecretStoreCapabilities
  482. }{
  483. {name: "read_only", caps: esv1.SecretStoreReadOnly, expectedPB: pb.SecretStoreCapabilities_READ_ONLY},
  484. {name: "write_only", caps: esv1.SecretStoreWriteOnly, expectedPB: pb.SecretStoreCapabilities_WRITE_ONLY},
  485. {name: "read_write", caps: esv1.SecretStoreReadWrite, expectedPB: pb.SecretStoreCapabilities_READ_WRITE},
  486. }
  487. for _, tc := range testCases {
  488. t.Run(tc.name, func(t *testing.T) {
  489. server := NewServer(nil, ProviderMapping{
  490. schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
  491. caps: tc.caps,
  492. newClient: func(context.Context, esv1.GenericStore, client.Client, string) (esv1.SecretsClient, error) {
  493. return &fakeSecretsClient{}, nil
  494. },
  495. },
  496. }, (&specMapperRecorder{}).mapRef)
  497. resp, err := server.Capabilities(context.Background(), &pb.CapabilitiesRequest{
  498. ProviderRef: &pb.ProviderReference{
  499. ApiVersion: "provider.external-secrets.io/v2alpha1",
  500. Kind: "Fake",
  501. Name: "backend",
  502. },
  503. })
  504. if err != nil {
  505. t.Fatalf("Capabilities() error = %v", err)
  506. }
  507. if resp.Capabilities != tc.expectedPB {
  508. t.Fatalf("expected %v, got %v", tc.expectedPB, resp.Capabilities)
  509. }
  510. })
  511. }
  512. }
  513. func TestServerRejectsInvalidRequests(t *testing.T) {
  514. server := NewServer(nil, ProviderMapping{}, (&specMapperRecorder{}).mapRef)
  515. testCases := []struct {
  516. name string
  517. call func() error
  518. want string
  519. }{
  520. {
  521. name: "get_secret_nil_request",
  522. call: func() error {
  523. _, err := server.GetSecret(context.Background(), nil)
  524. return err
  525. },
  526. want: "request or remote ref is nil",
  527. },
  528. {
  529. name: "get_secret_empty_source_namespace",
  530. call: func() error {
  531. _, err := server.GetSecret(context.Background(), &pb.GetSecretRequest{
  532. RemoteRef: &pb.ExternalSecretDataRemoteRef{Key: "sample"},
  533. })
  534. return err
  535. },
  536. want: "source namespace is required",
  537. },
  538. {
  539. name: "push_secret_nil_payload",
  540. call: func() error {
  541. _, err := server.PushSecret(context.Background(), &pb.PushSecretRequest{
  542. SourceNamespace: serverTestSourceNamespace,
  543. })
  544. return err
  545. },
  546. want: "request or push secret data is nil",
  547. },
  548. {
  549. name: "validate_nil_request",
  550. call: func() error {
  551. _, err := server.Validate(context.Background(), nil)
  552. return err
  553. },
  554. want: "request is nil",
  555. },
  556. {
  557. name: "capabilities_nil_request",
  558. call: func() error {
  559. _, err := server.Capabilities(context.Background(), nil)
  560. return err
  561. },
  562. want: "request is nil",
  563. },
  564. }
  565. for _, tc := range testCases {
  566. t.Run(tc.name, func(t *testing.T) {
  567. err := tc.call()
  568. if err == nil || err.Error() != tc.want {
  569. t.Fatalf("expected %q, got %v", tc.want, err)
  570. }
  571. })
  572. }
  573. }
  574. func runValidateTest(t *testing.T, result esv1.ValidationResult, validateErr error) *pb.ValidateResponse {
  575. t.Helper()
  576. server := NewServer(nil, ProviderMapping{
  577. schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
  578. caps: esv1.SecretStoreReadWrite,
  579. newClient: func(context.Context, esv1.GenericStore, client.Client, string) (esv1.SecretsClient, error) {
  580. return &fakeSecretsClient{
  581. validateResult: result,
  582. validateErr: validateErr,
  583. }, nil
  584. },
  585. },
  586. }, (&specMapperRecorder{
  587. spec: &esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{Fake: &esv1.FakeProvider{}}},
  588. }).mapRef)
  589. resp, err := server.Validate(context.Background(), &pb.ValidateRequest{
  590. ProviderRef: &pb.ProviderReference{
  591. ApiVersion: "provider.external-secrets.io/v2alpha1",
  592. Kind: "Fake",
  593. Name: "backend",
  594. },
  595. SourceNamespace: "tenant-a",
  596. })
  597. if err != nil {
  598. t.Fatalf("Validate() error = %v", err)
  599. }
  600. return resp
  601. }