provider_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. // /*
  14. // Licensed under the Apache License, Version 2.0 (the "License");
  15. // you may not use this file except in compliance with the License.
  16. // You may obtain a copy of the License at
  17. //
  18. // https://www.apache.org/licenses/LICENSE-2.0
  19. //
  20. // Unless required by applicable law or agreed to in writing, software
  21. // distributed under the License is distributed on an "AS IS" BASIS,
  22. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  23. // See the License for the specific language governing permissions and
  24. // limitations under the License.
  25. // */
  26. package gitlab
  27. import (
  28. "context"
  29. "encoding/pem"
  30. "net/http"
  31. "net/http/httptest"
  32. "testing"
  33. "github.com/stretchr/testify/assert"
  34. corev1 "k8s.io/api/core/v1"
  35. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  36. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  37. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  38. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  39. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  40. )
  41. func TestGetClientWithCABundle(t *testing.T) {
  42. // Create a mock TLS server that asserts a client certificate is present
  43. server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  44. // We expect a GET request to the variables API
  45. assert.Equal(t, "/api/v4/projects/1234/variables/test-secret", r.URL.Path)
  46. w.Header().Set("Content-Type", "application/json")
  47. w.WriteHeader(http.StatusOK)
  48. w.Write([]byte(`{}`))
  49. }))
  50. defer server.Close()
  51. // Define the GitLab provider with the CABundle
  52. provider := &esv1.GitlabProvider{
  53. URL: server.URL,
  54. ProjectID: "1234",
  55. CABundle: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}),
  56. Auth: esv1.GitlabAuth{
  57. SecretRef: esv1.GitlabSecretRef{
  58. AccessToken: esmeta.SecretKeySelector{
  59. Name: "gitlab-secret",
  60. Key: "token",
  61. },
  62. },
  63. },
  64. }
  65. // Create a fake Kubernetes client with the required secret
  66. secret := &corev1.Secret{
  67. ObjectMeta: metav1.ObjectMeta{
  68. Name: "gitlab-secret",
  69. Namespace: "default",
  70. },
  71. Data: map[string][]byte{
  72. "token": []byte("test-token"),
  73. },
  74. }
  75. fakeClient := clientfake.NewClientBuilder().WithObjects(secret).Build()
  76. // Create the gitlabBase struct
  77. gl := &gitlabBase{
  78. kube: fakeClient,
  79. store: provider,
  80. namespace: "default",
  81. }
  82. // We need to initialize the gitlab clients inside our gitlabBase struct
  83. client, err := gl.getClient(context.Background(), provider)
  84. assert.NoError(t, err)
  85. gl.projectsClient = client.Projects
  86. gl.projectVariablesClient = client.ProjectVariables
  87. gl.groupVariablesClient = client.GroupVariables
  88. // Call getVariables to trigger a network request to the mock server.
  89. // The request will only succeed if the custom CA is correctly configured.
  90. _, err = gl.getVariables(esv1.ExternalSecretDataRemoteRef{Key: "test-secret"}, nil)
  91. assert.NoError(t, err, "getVariables should succeed with the correct CA")
  92. }
  93. func TestGetClientWithInvalidCABundle(t *testing.T) {
  94. provider := &esv1.GitlabProvider{
  95. CABundle: []byte("invalid-ca-bundle"),
  96. Auth: esv1.GitlabAuth{
  97. SecretRef: esv1.GitlabSecretRef{
  98. AccessToken: esmeta.SecretKeySelector{
  99. Name: "gitlab-secret",
  100. Key: "token",
  101. },
  102. },
  103. },
  104. }
  105. secret := &corev1.Secret{
  106. ObjectMeta: metav1.ObjectMeta{
  107. Name: "gitlab-secret",
  108. Namespace: "default",
  109. },
  110. Data: map[string][]byte{
  111. "token": []byte("test-token"),
  112. },
  113. }
  114. fakeClient := clientfake.NewClientBuilder().WithObjects(secret).Build()
  115. gl := &gitlabBase{
  116. kube: fakeClient,
  117. store: provider,
  118. namespace: "default",
  119. }
  120. _, err := gl.getClient(context.Background(), provider)
  121. assert.Error(t, err)
  122. assert.Contains(t, err.Error(), "failed to read ca bundle")
  123. }
  124. func TestGetClientWithCAProviderSecret(t *testing.T) {
  125. // Create a mock TLS server
  126. server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  127. w.Header().Set("Content-Type", "application/json")
  128. w.WriteHeader(http.StatusOK)
  129. w.Write([]byte(`{}`))
  130. }))
  131. defer server.Close()
  132. certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw})
  133. caSecret := makeFakeCASource(t, "Secret", "ca-secret", "default", certPEM)
  134. // Define the GitLab provider with the CAProvider
  135. provider := &esv1.GitlabProvider{
  136. URL: server.URL,
  137. ProjectID: "1234",
  138. Auth: esv1.GitlabAuth{
  139. SecretRef: esv1.GitlabSecretRef{
  140. AccessToken: esmeta.SecretKeySelector{
  141. Name: "gitlab-secret",
  142. Key: "token",
  143. },
  144. },
  145. },
  146. CAProvider: &esv1.CAProvider{
  147. Type: esv1.CAProviderTypeSecret,
  148. Name: "ca-secret",
  149. Key: "tls.crt",
  150. },
  151. }
  152. // Create a fake Kubernetes client with the required secrets
  153. accessTokenSecret := &corev1.Secret{
  154. ObjectMeta: metav1.ObjectMeta{
  155. Name: "gitlab-secret",
  156. Namespace: "default",
  157. },
  158. Data: map[string][]byte{
  159. "token": []byte("test-token"),
  160. },
  161. }
  162. fakeClient := clientfake.NewClientBuilder().WithObjects(accessTokenSecret, caSecret).Build()
  163. // Create the gitlabBase struct
  164. gl := &gitlabBase{
  165. kube: fakeClient,
  166. store: provider,
  167. namespace: "default",
  168. }
  169. // We need to initialize the gitlab clients inside our gitlabBase struct
  170. client, err := gl.getClient(context.Background(), provider)
  171. assert.NoError(t, err)
  172. gl.projectsClient = client.Projects
  173. gl.projectVariablesClient = client.ProjectVariables
  174. gl.groupVariablesClient = client.GroupVariables
  175. // Call getVariables to trigger a network request to the mock server.
  176. _, err = gl.getVariables(esv1.ExternalSecretDataRemoteRef{Key: "test-secret"}, nil)
  177. assert.NoError(t, err, "getVariables should succeed with the correct CA from Secret")
  178. }
  179. func TestGetClientWithCAProviderConfigMap(t *testing.T) {
  180. // Create a mock TLS server
  181. server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  182. w.Header().Set("Content-Type", "application/json")
  183. w.WriteHeader(http.StatusOK)
  184. w.Write([]byte(`{}`))
  185. }))
  186. defer server.Close()
  187. certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw})
  188. caCM := makeFakeCASource(t, "ConfigMap", "ca-cm", "default", certPEM)
  189. // Define the GitLab provider with the CAProvider
  190. provider := &esv1.GitlabProvider{
  191. URL: server.URL,
  192. ProjectID: "1234",
  193. Auth: esv1.GitlabAuth{
  194. SecretRef: esv1.GitlabSecretRef{
  195. AccessToken: esmeta.SecretKeySelector{
  196. Name: "gitlab-secret",
  197. Key: "token",
  198. },
  199. },
  200. },
  201. CAProvider: &esv1.CAProvider{
  202. Type: esv1.CAProviderTypeConfigMap,
  203. Name: "ca-cm",
  204. Key: "ca.crt",
  205. },
  206. }
  207. // Create a fake Kubernetes client with the required secrets
  208. accessTokenSecret := &corev1.Secret{
  209. ObjectMeta: metav1.ObjectMeta{
  210. Name: "gitlab-secret",
  211. Namespace: "default",
  212. },
  213. Data: map[string][]byte{
  214. "token": []byte("test-token"),
  215. },
  216. }
  217. fakeClient := clientfake.NewClientBuilder().WithObjects(accessTokenSecret, caCM).Build()
  218. // Create the gitlabBase struct
  219. gl := &gitlabBase{
  220. kube: fakeClient,
  221. store: provider,
  222. namespace: "default",
  223. }
  224. // We need to initialize the gitlab clients inside our gitlabBase struct
  225. client, err := gl.getClient(context.Background(), provider)
  226. assert.NoError(t, err)
  227. gl.projectsClient = client.Projects
  228. gl.projectVariablesClient = client.ProjectVariables
  229. gl.groupVariablesClient = client.GroupVariables
  230. // Call getVariables to trigger a network request to the mock server.
  231. _, err = gl.getVariables(esv1.ExternalSecretDataRemoteRef{Key: "test-secret"}, nil)
  232. assert.NoError(t, err, "getVariables should succeed with the correct CA from ConfigMap")
  233. }
  234. func makeFakeCASource(t *testing.T, kind, name, namespace string, certData []byte) kclient.Object {
  235. t.Helper()
  236. switch kind {
  237. case "Secret":
  238. return &corev1.Secret{
  239. ObjectMeta: metav1.ObjectMeta{
  240. Name: name,
  241. Namespace: namespace,
  242. },
  243. Data: map[string][]byte{
  244. "tls.crt": certData,
  245. },
  246. }
  247. case "ConfigMap":
  248. return &corev1.ConfigMap{
  249. ObjectMeta: metav1.ObjectMeta{
  250. Name: name,
  251. Namespace: namespace,
  252. },
  253. Data: map[string]string{
  254. "ca.crt": string(certData),
  255. },
  256. }
  257. }
  258. return nil
  259. }