pushsecret_controller_v2_test.go 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316
  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 pushsecret
  14. import (
  15. "context"
  16. "crypto/rand"
  17. "crypto/rsa"
  18. "crypto/tls"
  19. "crypto/x509"
  20. "crypto/x509/pkix"
  21. "encoding/pem"
  22. "math/big"
  23. "net"
  24. "testing"
  25. "time"
  26. "github.com/go-logr/logr"
  27. "google.golang.org/grpc"
  28. "google.golang.org/grpc/credentials"
  29. corev1 "k8s.io/api/core/v1"
  30. apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  31. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  32. "k8s.io/apimachinery/pkg/runtime"
  33. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  34. clientgoscheme "k8s.io/client-go/kubernetes/scheme"
  35. fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
  36. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  37. esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  38. pb "github.com/external-secrets/external-secrets/proto/provider"
  39. "github.com/external-secrets/external-secrets/runtime/clientmanager"
  40. )
  41. type pushsecretRecordingProviderServer struct {
  42. pb.UnimplementedSecretStoreProviderServer
  43. pushRequest *pb.PushSecretRequest
  44. deleteRequest *pb.DeleteSecretRequest
  45. }
  46. const (
  47. pushSecretManifestNamespace = "tenant-a"
  48. pushSecretRemoteKey = "remote/path"
  49. pushSecretProperty = "property"
  50. pushSecretSecretKey = "token"
  51. )
  52. func (s *pushsecretRecordingProviderServer) PushSecret(_ context.Context, req *pb.PushSecretRequest) (*pb.PushSecretResponse, error) {
  53. s.pushRequest = req
  54. return &pb.PushSecretResponse{}, nil
  55. }
  56. func (s *pushsecretRecordingProviderServer) DeleteSecret(_ context.Context, req *pb.DeleteSecretRequest) (*pb.DeleteSecretResponse, error) {
  57. s.deleteRequest = req
  58. return &pb.DeleteSecretResponse{}, nil
  59. }
  60. func (s *pushsecretRecordingProviderServer) SecretExists(_ context.Context, _ *pb.SecretExistsRequest) (*pb.SecretExistsResponse, error) {
  61. return &pb.SecretExistsResponse{Exists: false}, nil
  62. }
  63. func TestResolvedStoreInfoSupportsProviderKinds(t *testing.T) {
  64. providerInfo, ok := resolvedStoreInfo(esapi.PushSecretStoreRef{
  65. Name: "provider",
  66. Kind: esv1.ProviderKindStr,
  67. }, &esv1.Provider{
  68. ObjectMeta: metav1.ObjectMeta{
  69. Name: "provider",
  70. Labels: map[string]string{"team": "a"},
  71. },
  72. })
  73. if !ok {
  74. t.Fatal("expected provider store info to resolve")
  75. }
  76. if providerInfo.Name != "provider" || providerInfo.Kind != esv1.ProviderKindStr || providerInfo.Labels["team"] != "a" {
  77. t.Fatalf("unexpected provider info: %#v", providerInfo)
  78. }
  79. clusterProviderInfo, ok := resolvedStoreInfo(esapi.PushSecretStoreRef{
  80. Name: "cluster-provider",
  81. Kind: esv1.ClusterProviderKindStr,
  82. }, &esv1.ClusterProvider{
  83. ObjectMeta: metav1.ObjectMeta{
  84. Name: "cluster-provider",
  85. Labels: map[string]string{"scope": "cluster"},
  86. },
  87. })
  88. if !ok {
  89. t.Fatal("expected cluster provider store info to resolve")
  90. }
  91. if clusterProviderInfo.Name != "cluster-provider" || clusterProviderInfo.Kind != esv1.ClusterProviderKindStr || clusterProviderInfo.Labels["scope"] != "cluster" {
  92. t.Fatalf("unexpected cluster provider info: %#v", clusterProviderInfo)
  93. }
  94. }
  95. func TestResolvedStoreInfoInfersOmittedProviderKinds(t *testing.T) {
  96. providerInfo, ok := resolvedStoreInfo(esapi.PushSecretStoreRef{
  97. Name: "provider",
  98. }, &esv1.Provider{
  99. ObjectMeta: metav1.ObjectMeta{
  100. Name: "provider",
  101. Labels: map[string]string{"team": "a"},
  102. },
  103. })
  104. if !ok {
  105. t.Fatal("expected provider store info to resolve")
  106. }
  107. if providerInfo.Kind != esv1.ProviderKindStr {
  108. t.Fatalf("expected kind %q, got %#v", esv1.ProviderKindStr, providerInfo)
  109. }
  110. clusterProviderInfo, ok := resolvedStoreInfo(esapi.PushSecretStoreRef{
  111. Name: "cluster-provider",
  112. }, &esv1.ClusterProvider{
  113. ObjectMeta: metav1.ObjectMeta{
  114. Name: "cluster-provider",
  115. Labels: map[string]string{"scope": "cluster"},
  116. },
  117. })
  118. if !ok {
  119. t.Fatal("expected cluster provider store info to resolve")
  120. }
  121. if clusterProviderInfo.Kind != esv1.ClusterProviderKindStr {
  122. t.Fatalf("expected kind %q, got %#v", esv1.ClusterProviderKindStr, clusterProviderInfo)
  123. }
  124. }
  125. func TestValidateDataToMatchesResolvedStoresSupportsProviderKinds(t *testing.T) {
  126. err := validateDataToMatchesResolvedStores([]esapi.PushSecretDataTo{
  127. {
  128. StoreRef: &esapi.PushSecretStoreRef{
  129. Kind: esv1.ProviderKindStr,
  130. LabelSelector: &metav1.LabelSelector{
  131. MatchLabels: map[string]string{"team": "a"},
  132. },
  133. },
  134. RemoteKey: "bundle",
  135. },
  136. }, []storeInfo{
  137. {Name: "provider", Kind: esv1.ProviderKindStr, Labels: map[string]string{"team": "a"}},
  138. })
  139. if err != nil {
  140. t.Fatalf("expected provider label selector to match, got %v", err)
  141. }
  142. err = validateDataToMatchesResolvedStores([]esapi.PushSecretDataTo{
  143. {
  144. StoreRef: &esapi.PushSecretStoreRef{
  145. Kind: esv1.ClusterProviderKindStr,
  146. LabelSelector: &metav1.LabelSelector{
  147. MatchLabels: map[string]string{"scope": "missing"},
  148. },
  149. },
  150. RemoteKey: "bundle",
  151. },
  152. }, []storeInfo{
  153. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr, Labels: map[string]string{"scope": "cluster"}},
  154. })
  155. if err == nil || err.Error() != "dataTo[0]: labelSelector does not match any store in secretStoreRefs" {
  156. t.Fatalf("unexpected error: %v", err)
  157. }
  158. }
  159. func TestPushSecretToProvidersV2UsesProviderPath(t *testing.T) {
  160. previous := clientmanager.V2ProvidersEnabled()
  161. clientmanager.SetV2ProvidersEnabled(true)
  162. t.Cleanup(func() {
  163. clientmanager.SetV2ProvidersEnabled(previous)
  164. })
  165. scheme := newPushSecretTestScheme(t)
  166. server, address, tlsSecret := newPushSecretProviderServer(t)
  167. provider := &esv1.Provider{
  168. ObjectMeta: metav1.ObjectMeta{
  169. Name: "provider",
  170. Namespace: "tenant-a",
  171. Labels: map[string]string{"team": "a"},
  172. },
  173. Spec: esv1.ProviderSpec{
  174. Config: esv1.ProviderConfig{
  175. Address: address,
  176. ProviderRef: esv1.ProviderReference{
  177. APIVersion: "provider.external-secrets.io/v2alpha1",
  178. Kind: "Kubernetes",
  179. Name: "backend",
  180. },
  181. },
  182. },
  183. }
  184. kubeClient := fakeclient.NewClientBuilder().
  185. WithScheme(scheme).
  186. WithObjects(provider, &corev1.Secret{
  187. ObjectMeta: metav1.ObjectMeta{
  188. Name: "external-secrets-provider-tls",
  189. Namespace: "tenant-a",
  190. },
  191. Data: tlsSecret,
  192. }).
  193. Build()
  194. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  195. mgr := clientmanager.NewManager(kubeClient, "", false)
  196. defer func() {
  197. _ = mgr.Close(context.Background())
  198. }()
  199. ps := esapi.PushSecret{
  200. ObjectMeta: metav1.ObjectMeta{
  201. Name: "pushsecret",
  202. Namespace: "tenant-a",
  203. },
  204. Spec: esapi.PushSecretSpec{
  205. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  206. Name: "provider",
  207. Kind: esv1.ProviderKindStr,
  208. }},
  209. Data: []esapi.PushSecretData{{
  210. Match: esapi.PushSecretMatch{
  211. SecretKey: pushSecretSecretKey,
  212. RemoteRef: esapi.PushSecretRemoteRef{
  213. RemoteKey: pushSecretRemoteKey,
  214. Property: pushSecretProperty,
  215. },
  216. },
  217. Metadata: &apiextensionsv1.JSON{Raw: []byte(`{"owner":"eso"}`)},
  218. }},
  219. },
  220. }
  221. secret := &corev1.Secret{
  222. Data: map[string][]byte{pushSecretSecretKey: []byte("value")},
  223. }
  224. synced, err := r.PushSecretToProvidersV2(context.Background(), map[esapi.PushSecretStoreRef]any{
  225. {Name: "provider", Kind: esv1.ProviderKindStr}: provider,
  226. }, ps, secret, mgr)
  227. if err != nil {
  228. t.Fatalf("PushSecretToProvidersV2() error = %v", err)
  229. }
  230. if server.pushRequest == nil {
  231. t.Fatal("expected push request to be recorded")
  232. }
  233. if server.pushRequest.SourceNamespace != pushSecretManifestNamespace {
  234. t.Fatalf("unexpected source namespace: %q", server.pushRequest.SourceNamespace)
  235. }
  236. if server.pushRequest.ProviderRef == nil || server.pushRequest.ProviderRef.Name != "backend" {
  237. t.Fatalf("unexpected provider ref: %#v", server.pushRequest.ProviderRef)
  238. }
  239. if string(server.pushRequest.SecretData[pushSecretSecretKey]) != "value" {
  240. t.Fatalf("unexpected secret data: %#v", server.pushRequest.SecretData)
  241. }
  242. if server.pushRequest.PushSecretData == nil || server.pushRequest.PushSecretData.RemoteKey != pushSecretRemoteKey || server.pushRequest.PushSecretData.Property != pushSecretProperty {
  243. t.Fatalf("unexpected push payload: %#v", server.pushRequest.PushSecretData)
  244. }
  245. if string(server.pushRequest.PushSecretData.Metadata) != `{"owner":"eso"}` {
  246. t.Fatalf("unexpected metadata: %q", string(server.pushRequest.PushSecretData.Metadata))
  247. }
  248. if synced["Provider/provider"]["remote/path/property"].Match.SecretKey != pushSecretSecretKey {
  249. t.Fatalf("unexpected synced map: %#v", synced)
  250. }
  251. }
  252. func TestPushSecretToProvidersV2UsesProviderPathWhenKindOmitted(t *testing.T) {
  253. previous := clientmanager.V2ProvidersEnabled()
  254. clientmanager.SetV2ProvidersEnabled(true)
  255. t.Cleanup(func() {
  256. clientmanager.SetV2ProvidersEnabled(previous)
  257. })
  258. scheme := newPushSecretTestScheme(t)
  259. server, address, tlsSecret := newPushSecretProviderServer(t)
  260. provider := &esv1.Provider{
  261. ObjectMeta: metav1.ObjectMeta{
  262. Name: "provider",
  263. Namespace: pushSecretManifestNamespace,
  264. Labels: map[string]string{"team": "a"},
  265. },
  266. Spec: esv1.ProviderSpec{
  267. Config: esv1.ProviderConfig{
  268. Address: address,
  269. ProviderRef: esv1.ProviderReference{
  270. APIVersion: "provider.external-secrets.io/v2alpha1",
  271. Kind: "Kubernetes",
  272. Name: "backend",
  273. },
  274. },
  275. },
  276. }
  277. kubeClient := fakeclient.NewClientBuilder().
  278. WithScheme(scheme).
  279. WithObjects(provider, &corev1.Secret{
  280. ObjectMeta: metav1.ObjectMeta{
  281. Name: "external-secrets-provider-tls",
  282. Namespace: pushSecretManifestNamespace,
  283. },
  284. Data: tlsSecret,
  285. }).
  286. Build()
  287. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  288. mgr := clientmanager.NewManager(kubeClient, "", false)
  289. defer func() {
  290. _ = mgr.Close(context.Background())
  291. }()
  292. ps := esapi.PushSecret{
  293. ObjectMeta: metav1.ObjectMeta{
  294. Name: "pushsecret",
  295. Namespace: "tenant-a",
  296. },
  297. Spec: esapi.PushSecretSpec{
  298. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  299. Name: "provider",
  300. }},
  301. Data: []esapi.PushSecretData{{
  302. Match: esapi.PushSecretMatch{
  303. SecretKey: "token",
  304. RemoteRef: esapi.PushSecretRemoteRef{
  305. RemoteKey: "remote/path",
  306. Property: "property",
  307. },
  308. },
  309. Metadata: &apiextensionsv1.JSON{Raw: []byte(`{"owner":"eso"}`)},
  310. }},
  311. },
  312. }
  313. secret := &corev1.Secret{
  314. Data: map[string][]byte{"token": []byte("value")},
  315. }
  316. synced, err := r.PushSecretToProvidersV2(context.Background(), map[esapi.PushSecretStoreRef]any{
  317. {Name: "provider"}: provider,
  318. }, ps, secret, mgr)
  319. if err != nil {
  320. t.Fatalf("PushSecretToProvidersV2() error = %v", err)
  321. }
  322. if server.pushRequest == nil {
  323. t.Fatal("expected push request to be recorded")
  324. }
  325. if synced["Provider/provider"]["remote/path/property"].Match.SecretKey != "token" {
  326. t.Fatalf("unexpected synced map: %#v", synced)
  327. }
  328. }
  329. func TestPushSecretToProvidersV2UsesProviderNamespaceAuthScope(t *testing.T) {
  330. previous := clientmanager.V2ProvidersEnabled()
  331. clientmanager.SetV2ProvidersEnabled(true)
  332. t.Cleanup(func() {
  333. clientmanager.SetV2ProvidersEnabled(previous)
  334. })
  335. scheme := newPushSecretTestScheme(t)
  336. server, address, tlsSecret := newPushSecretProviderServer(t)
  337. const manifestNamespace = "tenant-a"
  338. const providerNamespace = "provider-config-ns"
  339. clusterProvider := &esv1.ClusterProvider{
  340. ObjectMeta: metav1.ObjectMeta{
  341. Name: "cluster-provider",
  342. Labels: map[string]string{"scope": "cluster"},
  343. },
  344. Spec: esv1.ClusterProviderSpec{
  345. Config: esv1.ProviderConfig{
  346. Address: address,
  347. ProviderRef: esv1.ProviderReference{
  348. APIVersion: "provider.external-secrets.io/v2alpha1",
  349. Kind: "Kubernetes",
  350. Name: "backend",
  351. Namespace: providerNamespace,
  352. },
  353. },
  354. AuthenticationScope: esv1.AuthenticationScopeProviderNamespace,
  355. Conditions: []esv1.ClusterSecretStoreCondition{{
  356. Namespaces: []string{manifestNamespace},
  357. }},
  358. },
  359. }
  360. kubeClient := fakeclient.NewClientBuilder().
  361. WithScheme(scheme).
  362. WithObjects(
  363. clusterProvider,
  364. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  365. Name: manifestNamespace,
  366. Labels: map[string]string{
  367. "kubernetes.io/metadata.name": manifestNamespace,
  368. },
  369. }},
  370. &corev1.Secret{
  371. ObjectMeta: metav1.ObjectMeta{
  372. Name: "external-secrets-provider-tls",
  373. Namespace: providerNamespace,
  374. },
  375. Data: tlsSecret,
  376. },
  377. ).
  378. Build()
  379. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  380. mgr := clientmanager.NewManager(kubeClient, "", false)
  381. defer func() {
  382. _ = mgr.Close(context.Background())
  383. }()
  384. ps := esapi.PushSecret{
  385. ObjectMeta: metav1.ObjectMeta{
  386. Name: "pushsecret",
  387. Namespace: manifestNamespace,
  388. },
  389. Spec: esapi.PushSecretSpec{
  390. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  391. Name: "cluster-provider",
  392. Kind: esv1.ClusterProviderKindStr,
  393. }},
  394. Data: []esapi.PushSecretData{{
  395. Match: esapi.PushSecretMatch{
  396. SecretKey: "token",
  397. RemoteRef: esapi.PushSecretRemoteRef{
  398. RemoteKey: "remote/path",
  399. Property: "property",
  400. },
  401. },
  402. Metadata: &apiextensionsv1.JSON{Raw: []byte(`{"owner":"eso"}`)},
  403. }},
  404. },
  405. }
  406. secret := &corev1.Secret{
  407. Data: map[string][]byte{"token": []byte("value")},
  408. }
  409. synced, err := r.PushSecretToProvidersV2(context.Background(), map[esapi.PushSecretStoreRef]any{
  410. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  411. }, ps, secret, mgr)
  412. if err != nil {
  413. t.Fatalf("PushSecretToProvidersV2() error = %v", err)
  414. }
  415. if server.pushRequest == nil {
  416. t.Fatal("expected push request to be recorded")
  417. }
  418. if server.pushRequest.SourceNamespace != providerNamespace {
  419. t.Fatalf("unexpected source namespace: %q", server.pushRequest.SourceNamespace)
  420. }
  421. if server.pushRequest.ProviderRef == nil || server.pushRequest.ProviderRef.Name != "backend" || server.pushRequest.ProviderRef.Namespace != providerNamespace {
  422. t.Fatalf("unexpected provider ref: %#v", server.pushRequest.ProviderRef)
  423. }
  424. if synced["ClusterProvider/cluster-provider"]["remote/path/property"].Match.SecretKey != "token" {
  425. t.Fatalf("unexpected synced map: %#v", synced)
  426. }
  427. }
  428. func TestDeleteSecretFromProvidersV2UsesClusterProviderPath(t *testing.T) {
  429. previous := clientmanager.V2ProvidersEnabled()
  430. clientmanager.SetV2ProvidersEnabled(true)
  431. t.Cleanup(func() {
  432. clientmanager.SetV2ProvidersEnabled(previous)
  433. })
  434. scheme := newPushSecretTestScheme(t)
  435. server, address, tlsSecret := newPushSecretProviderServer(t)
  436. clusterProvider := &esv1.ClusterProvider{
  437. ObjectMeta: metav1.ObjectMeta{
  438. Name: "cluster-provider",
  439. Labels: map[string]string{"scope": "cluster"},
  440. },
  441. Spec: esv1.ClusterProviderSpec{
  442. Config: esv1.ProviderConfig{
  443. Address: address,
  444. ProviderRef: esv1.ProviderReference{
  445. APIVersion: "provider.external-secrets.io/v2alpha1",
  446. Kind: "Kubernetes",
  447. Name: "backend",
  448. },
  449. },
  450. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  451. Conditions: []esv1.ClusterSecretStoreCondition{{
  452. Namespaces: []string{"tenant-a"},
  453. }},
  454. },
  455. }
  456. kubeClient := fakeclient.NewClientBuilder().
  457. WithScheme(scheme).
  458. WithObjects(
  459. clusterProvider,
  460. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  461. Name: "tenant-a",
  462. Labels: map[string]string{
  463. "kubernetes.io/metadata.name": "tenant-a",
  464. },
  465. }},
  466. &corev1.Secret{
  467. ObjectMeta: metav1.ObjectMeta{
  468. Name: "external-secrets-provider-tls",
  469. Namespace: "tenant-a",
  470. },
  471. Data: tlsSecret,
  472. },
  473. ).
  474. Build()
  475. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  476. ps := &esapi.PushSecret{
  477. ObjectMeta: metav1.ObjectMeta{
  478. Name: "pushsecret",
  479. Namespace: "tenant-a",
  480. },
  481. Status: esapi.PushSecretStatus{
  482. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  483. "ClusterProvider/cluster-provider": {
  484. "remote/path": {
  485. Match: esapi.PushSecretMatch{
  486. SecretKey: "token",
  487. RemoteRef: esapi.PushSecretRemoteRef{
  488. RemoteKey: "remote/path",
  489. Property: "property",
  490. },
  491. },
  492. },
  493. },
  494. },
  495. },
  496. }
  497. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{
  498. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  499. })
  500. if err != nil {
  501. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  502. }
  503. if server.deleteRequest == nil {
  504. t.Fatal("expected delete request to be recorded")
  505. }
  506. if server.deleteRequest.SourceNamespace != pushSecretManifestNamespace {
  507. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  508. }
  509. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != pushSecretRemoteKey || server.deleteRequest.RemoteRef.Property != pushSecretProperty {
  510. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  511. }
  512. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  513. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  514. }
  515. }
  516. func TestDeleteSecretFromProvidersV2UsesClusterProviderPathWhenKindOmitted(t *testing.T) {
  517. previous := clientmanager.V2ProvidersEnabled()
  518. clientmanager.SetV2ProvidersEnabled(true)
  519. t.Cleanup(func() {
  520. clientmanager.SetV2ProvidersEnabled(previous)
  521. })
  522. scheme := newPushSecretTestScheme(t)
  523. server, address, tlsSecret := newPushSecretProviderServer(t)
  524. clusterProvider := &esv1.ClusterProvider{
  525. ObjectMeta: metav1.ObjectMeta{
  526. Name: "cluster-provider",
  527. Labels: map[string]string{"scope": "cluster"},
  528. },
  529. Spec: esv1.ClusterProviderSpec{
  530. Config: esv1.ProviderConfig{
  531. Address: address,
  532. ProviderRef: esv1.ProviderReference{
  533. APIVersion: "provider.external-secrets.io/v2alpha1",
  534. Kind: "Kubernetes",
  535. Name: "backend",
  536. },
  537. },
  538. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  539. Conditions: []esv1.ClusterSecretStoreCondition{{
  540. Namespaces: []string{"tenant-a"},
  541. }},
  542. },
  543. }
  544. kubeClient := fakeclient.NewClientBuilder().
  545. WithScheme(scheme).
  546. WithObjects(
  547. clusterProvider,
  548. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  549. Name: "tenant-a",
  550. Labels: map[string]string{
  551. "kubernetes.io/metadata.name": "tenant-a",
  552. },
  553. }},
  554. &corev1.Secret{
  555. ObjectMeta: metav1.ObjectMeta{
  556. Name: "external-secrets-provider-tls",
  557. Namespace: "tenant-a",
  558. },
  559. Data: tlsSecret,
  560. },
  561. ).
  562. Build()
  563. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  564. ps := &esapi.PushSecret{
  565. ObjectMeta: metav1.ObjectMeta{
  566. Name: "pushsecret",
  567. Namespace: "tenant-a",
  568. },
  569. Status: esapi.PushSecretStatus{
  570. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  571. "ClusterProvider/cluster-provider": {
  572. "remote/path": {
  573. Match: esapi.PushSecretMatch{
  574. SecretKey: "token",
  575. RemoteRef: esapi.PushSecretRemoteRef{
  576. RemoteKey: "remote/path",
  577. Property: "property",
  578. },
  579. },
  580. },
  581. },
  582. },
  583. },
  584. }
  585. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{
  586. {Name: "cluster-provider"}: clusterProvider,
  587. })
  588. if err != nil {
  589. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  590. }
  591. if server.deleteRequest == nil {
  592. t.Fatal("expected delete request to be recorded")
  593. }
  594. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  595. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  596. }
  597. }
  598. func TestGetSecretStoresV2ResolvesClusterProviderWhenKindOmitted(t *testing.T) {
  599. previous := clientmanager.V2ProvidersEnabled()
  600. clientmanager.SetV2ProvidersEnabled(true)
  601. t.Cleanup(func() {
  602. clientmanager.SetV2ProvidersEnabled(previous)
  603. })
  604. scheme := newPushSecretTestScheme(t)
  605. clusterProvider := &esv1.ClusterProvider{
  606. ObjectMeta: metav1.ObjectMeta{
  607. Name: "cluster-provider",
  608. },
  609. }
  610. kubeClient := fakeclient.NewClientBuilder().
  611. WithScheme(scheme).
  612. WithObjects(clusterProvider).
  613. Build()
  614. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  615. ps := esapi.PushSecret{
  616. ObjectMeta: metav1.ObjectMeta{
  617. Name: "pushsecret",
  618. Namespace: "tenant-a",
  619. },
  620. Spec: esapi.PushSecretSpec{
  621. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  622. Name: "cluster-provider",
  623. }},
  624. },
  625. }
  626. stores, err := r.GetSecretStoresV2(context.Background(), ps)
  627. if err != nil {
  628. t.Fatalf("GetSecretStoresV2() error = %v", err)
  629. }
  630. store, ok := stores[esapi.PushSecretStoreRef{Name: "cluster-provider"}]
  631. if !ok {
  632. t.Fatalf("expected cluster provider store, got %#v", stores)
  633. }
  634. if _, ok := store.(*esv1.ClusterProvider); !ok {
  635. t.Fatalf("expected ClusterProvider, got %T", store)
  636. }
  637. }
  638. func TestGetSecretStoresV2SupportsSecretStoreLabelSelectors(t *testing.T) {
  639. scheme := newPushSecretTestScheme(t)
  640. selectedStore := &esv1.SecretStore{
  641. ObjectMeta: metav1.ObjectMeta{
  642. Name: "selected",
  643. Namespace: "tenant-a",
  644. Labels: map[string]string{"env": "test"},
  645. },
  646. }
  647. otherNamespaceStore := &esv1.SecretStore{
  648. ObjectMeta: metav1.ObjectMeta{
  649. Name: "other-namespace",
  650. Namespace: "tenant-b",
  651. Labels: map[string]string{"env": "test"},
  652. },
  653. }
  654. kubeClient := fakeclient.NewClientBuilder().
  655. WithScheme(scheme).
  656. WithObjects(selectedStore, otherNamespaceStore).
  657. Build()
  658. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  659. ps := esapi.PushSecret{
  660. ObjectMeta: metav1.ObjectMeta{
  661. Name: "pushsecret",
  662. Namespace: "tenant-a",
  663. },
  664. Spec: esapi.PushSecretSpec{
  665. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  666. Kind: esv1.SecretStoreKind,
  667. LabelSelector: &metav1.LabelSelector{
  668. MatchLabels: map[string]string{"env": "test"},
  669. },
  670. }},
  671. },
  672. }
  673. stores, err := r.GetSecretStoresV2(context.Background(), ps)
  674. if err != nil {
  675. t.Fatalf("GetSecretStoresV2() error = %v", err)
  676. }
  677. if len(stores) != 1 {
  678. t.Fatalf("expected one resolved store, got %#v", stores)
  679. }
  680. store, ok := stores[esapi.PushSecretStoreRef{Name: "selected", Kind: esv1.SecretStoreKind}]
  681. if !ok {
  682. t.Fatalf("expected selected store, got %#v", stores)
  683. }
  684. if _, ok := store.(*esv1.SecretStore); !ok {
  685. t.Fatalf("expected SecretStore, got %T", store)
  686. }
  687. }
  688. func TestDeleteSecretFromProvidersV2DeletesRemovedStoreEvenWhenNoLongerReferenced(t *testing.T) {
  689. previous := clientmanager.V2ProvidersEnabled()
  690. clientmanager.SetV2ProvidersEnabled(true)
  691. t.Cleanup(func() {
  692. clientmanager.SetV2ProvidersEnabled(previous)
  693. })
  694. scheme := newPushSecretTestScheme(t)
  695. server, address, tlsSecret := newPushSecretProviderServer(t)
  696. clusterProvider := &esv1.ClusterProvider{
  697. ObjectMeta: metav1.ObjectMeta{
  698. Name: "cluster-provider",
  699. },
  700. Spec: esv1.ClusterProviderSpec{
  701. Config: esv1.ProviderConfig{
  702. Address: address,
  703. ProviderRef: esv1.ProviderReference{
  704. APIVersion: "provider.external-secrets.io/v2alpha1",
  705. Kind: "Kubernetes",
  706. Name: "backend",
  707. },
  708. },
  709. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  710. Conditions: []esv1.ClusterSecretStoreCondition{{
  711. Namespaces: []string{"tenant-a"},
  712. }},
  713. },
  714. }
  715. kubeClient := fakeclient.NewClientBuilder().
  716. WithScheme(scheme).
  717. WithObjects(
  718. clusterProvider,
  719. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  720. Name: "tenant-a",
  721. Labels: map[string]string{
  722. "kubernetes.io/metadata.name": "tenant-a",
  723. },
  724. }},
  725. &corev1.Secret{
  726. ObjectMeta: metav1.ObjectMeta{
  727. Name: "external-secrets-provider-tls",
  728. Namespace: "tenant-a",
  729. },
  730. Data: tlsSecret,
  731. },
  732. ).
  733. Build()
  734. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  735. ps := &esapi.PushSecret{
  736. ObjectMeta: metav1.ObjectMeta{
  737. Name: "pushsecret",
  738. Namespace: "tenant-a",
  739. },
  740. Status: esapi.PushSecretStatus{
  741. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  742. "ClusterProvider/cluster-provider": {
  743. "remote/path": {
  744. Match: esapi.PushSecretMatch{
  745. SecretKey: "token",
  746. RemoteRef: esapi.PushSecretRemoteRef{
  747. RemoteKey: "remote/path",
  748. Property: "property",
  749. },
  750. },
  751. },
  752. },
  753. },
  754. },
  755. }
  756. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{})
  757. if err != nil {
  758. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  759. }
  760. if server.deleteRequest == nil {
  761. t.Fatal("expected delete request to be recorded")
  762. }
  763. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/path" {
  764. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  765. }
  766. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  767. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  768. }
  769. }
  770. func TestDeleteSecretFromProvidersV2UsesProviderNamespaceAuthScope(t *testing.T) {
  771. previous := clientmanager.V2ProvidersEnabled()
  772. clientmanager.SetV2ProvidersEnabled(true)
  773. t.Cleanup(func() {
  774. clientmanager.SetV2ProvidersEnabled(previous)
  775. })
  776. scheme := newPushSecretTestScheme(t)
  777. server, address, tlsSecret := newPushSecretProviderServer(t)
  778. const manifestNamespace = "tenant-a"
  779. const providerNamespace = "provider-config-ns"
  780. clusterProvider := &esv1.ClusterProvider{
  781. ObjectMeta: metav1.ObjectMeta{
  782. Name: "cluster-provider",
  783. Labels: map[string]string{"scope": "cluster"},
  784. },
  785. Spec: esv1.ClusterProviderSpec{
  786. Config: esv1.ProviderConfig{
  787. Address: address,
  788. ProviderRef: esv1.ProviderReference{
  789. APIVersion: "provider.external-secrets.io/v2alpha1",
  790. Kind: "Kubernetes",
  791. Name: "backend",
  792. Namespace: providerNamespace,
  793. },
  794. },
  795. AuthenticationScope: esv1.AuthenticationScopeProviderNamespace,
  796. Conditions: []esv1.ClusterSecretStoreCondition{{
  797. Namespaces: []string{manifestNamespace},
  798. }},
  799. },
  800. }
  801. kubeClient := fakeclient.NewClientBuilder().
  802. WithScheme(scheme).
  803. WithObjects(
  804. clusterProvider,
  805. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  806. Name: manifestNamespace,
  807. Labels: map[string]string{
  808. "kubernetes.io/metadata.name": manifestNamespace,
  809. },
  810. }},
  811. &corev1.Secret{
  812. ObjectMeta: metav1.ObjectMeta{
  813. Name: "external-secrets-provider-tls",
  814. Namespace: providerNamespace,
  815. },
  816. Data: tlsSecret,
  817. },
  818. ).
  819. Build()
  820. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  821. ps := &esapi.PushSecret{
  822. ObjectMeta: metav1.ObjectMeta{
  823. Name: "pushsecret",
  824. Namespace: manifestNamespace,
  825. },
  826. Status: esapi.PushSecretStatus{
  827. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  828. "ClusterProvider/cluster-provider": {
  829. "remote/path": {
  830. Match: esapi.PushSecretMatch{
  831. SecretKey: "token",
  832. RemoteRef: esapi.PushSecretRemoteRef{
  833. RemoteKey: "remote/path",
  834. Property: "property",
  835. },
  836. },
  837. },
  838. },
  839. },
  840. },
  841. }
  842. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{
  843. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  844. })
  845. if err != nil {
  846. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  847. }
  848. if server.deleteRequest == nil {
  849. t.Fatal("expected delete request to be recorded")
  850. }
  851. if server.deleteRequest.SourceNamespace != providerNamespace {
  852. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  853. }
  854. if server.deleteRequest.ProviderRef == nil || server.deleteRequest.ProviderRef.Namespace != providerNamespace {
  855. t.Fatalf("unexpected provider ref: %#v", server.deleteRequest.ProviderRef)
  856. }
  857. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  858. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  859. }
  860. }
  861. func TestDeleteSecretFromProvidersV2DeletesOnlyRemovedEntriesForManifestScope(t *testing.T) {
  862. previous := clientmanager.V2ProvidersEnabled()
  863. clientmanager.SetV2ProvidersEnabled(true)
  864. t.Cleanup(func() {
  865. clientmanager.SetV2ProvidersEnabled(previous)
  866. })
  867. scheme := newPushSecretTestScheme(t)
  868. server, address, tlsSecret := newPushSecretProviderServer(t)
  869. clusterProvider := &esv1.ClusterProvider{
  870. ObjectMeta: metav1.ObjectMeta{
  871. Name: "cluster-provider",
  872. },
  873. Spec: esv1.ClusterProviderSpec{
  874. Config: esv1.ProviderConfig{
  875. Address: address,
  876. ProviderRef: esv1.ProviderReference{
  877. APIVersion: "provider.external-secrets.io/v2alpha1",
  878. Kind: "Kubernetes",
  879. Name: "backend",
  880. },
  881. },
  882. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  883. Conditions: []esv1.ClusterSecretStoreCondition{{
  884. Namespaces: []string{"tenant-a"},
  885. }},
  886. },
  887. }
  888. kubeClient := fakeclient.NewClientBuilder().
  889. WithScheme(scheme).
  890. WithObjects(
  891. clusterProvider,
  892. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  893. Name: "tenant-a",
  894. Labels: map[string]string{
  895. "kubernetes.io/metadata.name": "tenant-a",
  896. },
  897. }},
  898. &corev1.Secret{
  899. ObjectMeta: metav1.ObjectMeta{
  900. Name: "external-secrets-provider-tls",
  901. Namespace: "tenant-a",
  902. },
  903. Data: tlsSecret,
  904. },
  905. ).
  906. Build()
  907. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  908. ps := &esapi.PushSecret{
  909. ObjectMeta: metav1.ObjectMeta{
  910. Name: "pushsecret",
  911. Namespace: "tenant-a",
  912. },
  913. Status: esapi.PushSecretStatus{
  914. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  915. "ClusterProvider/cluster-provider": {
  916. "remote/keep/property": {
  917. Match: esapi.PushSecretMatch{
  918. SecretKey: "keep",
  919. RemoteRef: esapi.PushSecretRemoteRef{
  920. RemoteKey: "remote/keep",
  921. Property: "property",
  922. },
  923. },
  924. },
  925. "remote/delete/property": {
  926. Match: esapi.PushSecretMatch{
  927. SecretKey: "delete",
  928. RemoteRef: esapi.PushSecretRemoteRef{
  929. RemoteKey: "remote/delete",
  930. Property: "property",
  931. },
  932. },
  933. },
  934. },
  935. },
  936. },
  937. }
  938. newMap := esapi.SyncedPushSecretsMap{
  939. "ClusterProvider/cluster-provider": {
  940. "remote/keep/property": ps.Status.SyncedPushSecrets["ClusterProvider/cluster-provider"]["remote/keep/property"],
  941. },
  942. }
  943. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, newMap, map[esapi.PushSecretStoreRef]any{
  944. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  945. })
  946. if err != nil {
  947. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  948. }
  949. if server.deleteRequest == nil {
  950. t.Fatal("expected delete request to be recorded")
  951. }
  952. if server.deleteRequest.SourceNamespace != "tenant-a" {
  953. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  954. }
  955. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/delete" || server.deleteRequest.RemoteRef.Property != "property" {
  956. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  957. }
  958. storeState, ok := result["ClusterProvider/cluster-provider"]
  959. if !ok {
  960. t.Fatalf("expected synced state for cluster provider, got %#v", result)
  961. }
  962. if len(storeState) != 1 {
  963. t.Fatalf("expected one remaining synced entry, got %#v", storeState)
  964. }
  965. if _, ok := storeState["remote/keep/property"]; !ok {
  966. t.Fatalf("expected keep entry to remain, got %#v", storeState)
  967. }
  968. if _, ok := storeState["remote/delete/property"]; ok {
  969. t.Fatalf("expected delete entry to be removed, got %#v", storeState)
  970. }
  971. }
  972. func TestDeleteSecretFromProvidersV2DeletesOnlyRemovedEntriesForProviderNamespaceScope(t *testing.T) {
  973. previous := clientmanager.V2ProvidersEnabled()
  974. clientmanager.SetV2ProvidersEnabled(true)
  975. t.Cleanup(func() {
  976. clientmanager.SetV2ProvidersEnabled(previous)
  977. })
  978. scheme := newPushSecretTestScheme(t)
  979. server, address, tlsSecret := newPushSecretProviderServer(t)
  980. const manifestNamespace = "tenant-a"
  981. const providerNamespace = "provider-config-ns"
  982. clusterProvider := &esv1.ClusterProvider{
  983. ObjectMeta: metav1.ObjectMeta{
  984. Name: "cluster-provider",
  985. },
  986. Spec: esv1.ClusterProviderSpec{
  987. Config: esv1.ProviderConfig{
  988. Address: address,
  989. ProviderRef: esv1.ProviderReference{
  990. APIVersion: "provider.external-secrets.io/v2alpha1",
  991. Kind: "Kubernetes",
  992. Name: "backend",
  993. Namespace: providerNamespace,
  994. },
  995. },
  996. AuthenticationScope: esv1.AuthenticationScopeProviderNamespace,
  997. Conditions: []esv1.ClusterSecretStoreCondition{{
  998. Namespaces: []string{manifestNamespace},
  999. }},
  1000. },
  1001. }
  1002. kubeClient := fakeclient.NewClientBuilder().
  1003. WithScheme(scheme).
  1004. WithObjects(
  1005. clusterProvider,
  1006. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  1007. Name: manifestNamespace,
  1008. Labels: map[string]string{
  1009. "kubernetes.io/metadata.name": manifestNamespace,
  1010. },
  1011. }},
  1012. &corev1.Secret{
  1013. ObjectMeta: metav1.ObjectMeta{
  1014. Name: "external-secrets-provider-tls",
  1015. Namespace: providerNamespace,
  1016. },
  1017. Data: tlsSecret,
  1018. },
  1019. ).
  1020. Build()
  1021. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  1022. ps := &esapi.PushSecret{
  1023. ObjectMeta: metav1.ObjectMeta{
  1024. Name: "pushsecret",
  1025. Namespace: manifestNamespace,
  1026. },
  1027. Status: esapi.PushSecretStatus{
  1028. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  1029. "ClusterProvider/cluster-provider": {
  1030. "remote/keep/property": {
  1031. Match: esapi.PushSecretMatch{
  1032. SecretKey: "keep",
  1033. RemoteRef: esapi.PushSecretRemoteRef{
  1034. RemoteKey: "remote/keep",
  1035. Property: "property",
  1036. },
  1037. },
  1038. },
  1039. "remote/delete/property": {
  1040. Match: esapi.PushSecretMatch{
  1041. SecretKey: "delete",
  1042. RemoteRef: esapi.PushSecretRemoteRef{
  1043. RemoteKey: "remote/delete",
  1044. Property: "property",
  1045. },
  1046. },
  1047. },
  1048. },
  1049. },
  1050. },
  1051. }
  1052. newMap := esapi.SyncedPushSecretsMap{
  1053. "ClusterProvider/cluster-provider": {
  1054. "remote/keep/property": ps.Status.SyncedPushSecrets["ClusterProvider/cluster-provider"]["remote/keep/property"],
  1055. },
  1056. }
  1057. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, newMap, map[esapi.PushSecretStoreRef]any{
  1058. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  1059. })
  1060. if err != nil {
  1061. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  1062. }
  1063. if server.deleteRequest == nil {
  1064. t.Fatal("expected delete request to be recorded")
  1065. }
  1066. if server.deleteRequest.SourceNamespace != providerNamespace {
  1067. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  1068. }
  1069. if server.deleteRequest.ProviderRef == nil || server.deleteRequest.ProviderRef.Namespace != providerNamespace {
  1070. t.Fatalf("unexpected provider ref: %#v", server.deleteRequest.ProviderRef)
  1071. }
  1072. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/delete" || server.deleteRequest.RemoteRef.Property != "property" {
  1073. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  1074. }
  1075. storeState, ok := result["ClusterProvider/cluster-provider"]
  1076. if !ok {
  1077. t.Fatalf("expected synced state for cluster provider, got %#v", result)
  1078. }
  1079. if len(storeState) != 1 {
  1080. t.Fatalf("expected one remaining synced entry, got %#v", storeState)
  1081. }
  1082. if _, ok := storeState["remote/keep/property"]; !ok {
  1083. t.Fatalf("expected keep entry to remain, got %#v", storeState)
  1084. }
  1085. if _, ok := storeState["remote/delete/property"]; ok {
  1086. t.Fatalf("expected delete entry to be removed, got %#v", storeState)
  1087. }
  1088. }
  1089. func newPushSecretTestScheme(t *testing.T) *runtime.Scheme {
  1090. t.Helper()
  1091. scheme := runtime.NewScheme()
  1092. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  1093. utilruntime.Must(esv1.AddToScheme(scheme))
  1094. utilruntime.Must(esapi.AddToScheme(scheme))
  1095. return scheme
  1096. }
  1097. func newPushSecretProviderServer(t *testing.T) (*pushsecretRecordingProviderServer, string, map[string][]byte) {
  1098. t.Helper()
  1099. serverCert, serverKey, clientCert, clientKey, caCert := newPushSecretTLSArtifacts(t, "127.0.0.1")
  1100. caPool := x509.NewCertPool()
  1101. if !caPool.AppendCertsFromPEM(caCert) {
  1102. t.Fatal("failed to append CA cert")
  1103. }
  1104. tlsCert, err := tls.X509KeyPair(serverCert, serverKey)
  1105. if err != nil {
  1106. t.Fatalf("X509KeyPair() error = %v", err)
  1107. }
  1108. lis, err := net.Listen("tcp", "127.0.0.1:0")
  1109. if err != nil {
  1110. t.Fatalf("Listen() error = %v", err)
  1111. }
  1112. server := &pushsecretRecordingProviderServer{}
  1113. grpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(&tls.Config{
  1114. MinVersion: tls.VersionTLS12,
  1115. Certificates: []tls.Certificate{tlsCert},
  1116. ClientCAs: caPool,
  1117. ClientAuth: tls.RequireAndVerifyClientCert,
  1118. })))
  1119. pb.RegisterSecretStoreProviderServer(grpcServer, server)
  1120. go func() {
  1121. _ = grpcServer.Serve(lis)
  1122. }()
  1123. t.Cleanup(func() {
  1124. grpcServer.Stop()
  1125. _ = lis.Close()
  1126. })
  1127. return server, lis.Addr().String(), map[string][]byte{
  1128. "ca.crt": caCert,
  1129. "client.crt": clientCert,
  1130. "client.key": clientKey,
  1131. }
  1132. }
  1133. func newPushSecretTLSArtifacts(t *testing.T, host string) (serverCertPEM, serverKeyPEM, clientCertPEM, clientKeyPEM, caCertPEM []byte) {
  1134. t.Helper()
  1135. caKey, err := rsa.GenerateKey(rand.Reader, 2048)
  1136. if err != nil {
  1137. t.Fatalf("GenerateKey() error = %v", err)
  1138. }
  1139. caTemplate := &x509.Certificate{
  1140. SerialNumber: big.NewInt(1),
  1141. Subject: pkix.Name{
  1142. CommonName: "pushsecret-test-ca",
  1143. },
  1144. NotBefore: time.Now().Add(-time.Hour),
  1145. NotAfter: time.Now().Add(24 * time.Hour),
  1146. KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
  1147. BasicConstraintsValid: true,
  1148. IsCA: true,
  1149. }
  1150. caDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey)
  1151. if err != nil {
  1152. t.Fatalf("CreateCertificate() error = %v", err)
  1153. }
  1154. caCert, err := x509.ParseCertificate(caDER)
  1155. if err != nil {
  1156. t.Fatalf("ParseCertificate() error = %v", err)
  1157. }
  1158. serverCertPEM, serverKeyPEM = newPushSecretSignedTLSCert(t, caCert, caKey, 2, host, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
  1159. clientCertPEM, clientKeyPEM = newPushSecretSignedTLSCert(t, caCert, caKey, 3, host, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth})
  1160. caCertPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caDER})
  1161. return serverCertPEM, serverKeyPEM, clientCertPEM, clientKeyPEM, caCertPEM
  1162. }
  1163. func newPushSecretSignedTLSCert(t *testing.T, caCert *x509.Certificate, caKey *rsa.PrivateKey, serial int64, host string, usages []x509.ExtKeyUsage) ([]byte, []byte) {
  1164. t.Helper()
  1165. key, err := rsa.GenerateKey(rand.Reader, 2048)
  1166. if err != nil {
  1167. t.Fatalf("GenerateKey() error = %v", err)
  1168. }
  1169. template := &x509.Certificate{
  1170. SerialNumber: big.NewInt(serial),
  1171. Subject: pkix.Name{
  1172. CommonName: host,
  1173. },
  1174. NotBefore: time.Now().Add(-time.Hour),
  1175. NotAfter: time.Now().Add(24 * time.Hour),
  1176. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
  1177. ExtKeyUsage: usages,
  1178. }
  1179. if ip := net.ParseIP(host); ip != nil {
  1180. template.IPAddresses = []net.IP{ip}
  1181. } else {
  1182. template.DNSNames = []string{host}
  1183. }
  1184. der, err := x509.CreateCertificate(rand.Reader, template, caCert, &key.PublicKey, caKey)
  1185. if err != nil {
  1186. t.Fatalf("CreateCertificate() error = %v", err)
  1187. }
  1188. return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}),
  1189. pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
  1190. }