eso_v2.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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 addon
  13. import (
  14. "context"
  15. "fmt"
  16. "time"
  17. "github.com/external-secrets/external-secrets-e2e/framework/log"
  18. appsv1 "k8s.io/api/core/v1"
  19. rbacv1 "k8s.io/api/rbac/v1"
  20. "k8s.io/apimachinery/pkg/api/errors"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/util/wait"
  23. "k8s.io/client-go/kubernetes"
  24. )
  25. const (
  26. v2Namespace = "external-secrets-system"
  27. v2ControllerName = "external-secrets-v2"
  28. v2ProviderName = "kubernetes-provider"
  29. )
  30. // ESOV2 is an addon that installs External Secrets Operator V2 with Kubernetes provider.
  31. type ESOV2 struct {
  32. config *Config
  33. kubeClientSet kubernetes.Interface
  34. }
  35. // Setup installs ESO V2 controller and Kubernetes provider.
  36. func (e *ESOV2) Setup(config *Config) error {
  37. e.config = config
  38. e.kubeClientSet = config.KubeClientSet
  39. log.Logf("installing External Secrets Operator V2")
  40. // Create namespace
  41. if err := e.createNamespace(); err != nil {
  42. return fmt.Errorf("failed to create namespace: %w", err)
  43. }
  44. // Install CRDs
  45. if err := e.installCRDs(); err != nil {
  46. return fmt.Errorf("failed to install CRDs: %w", err)
  47. }
  48. // Create RBAC
  49. if err := e.createRBAC(); err != nil {
  50. return fmt.Errorf("failed to create RBAC: %w", err)
  51. }
  52. // Deploy controller
  53. if err := e.deployController(); err != nil {
  54. return fmt.Errorf("failed to deploy controller: %w", err)
  55. }
  56. // Deploy Kubernetes provider
  57. if err := e.deployKubernetesProvider(); err != nil {
  58. return fmt.Errorf("failed to deploy Kubernetes provider: %w", err)
  59. }
  60. // Wait for deployments to be ready
  61. if err := e.waitForDeployments(); err != nil {
  62. return fmt.Errorf("failed waiting for deployments: %w", err)
  63. }
  64. log.Logf("External Secrets Operator V2 installed successfully")
  65. return nil
  66. }
  67. func (e *ESOV2) createNamespace() error {
  68. ns := &appsv1.Namespace{
  69. ObjectMeta: metav1.ObjectMeta{
  70. Name: v2Namespace,
  71. },
  72. }
  73. _, err := e.kubeClientSet.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{})
  74. if err != nil && !isAlreadyExists(err) {
  75. return err
  76. }
  77. log.Logf("created namespace: %s", v2Namespace)
  78. return nil
  79. }
  80. func (e *ESOV2) installCRDs() error {
  81. // In a real implementation, this would apply actual CRD manifests
  82. // For now, we'll assume CRDs are already installed or use the Helm chart
  83. log.Logf("CRDs installation (assuming pre-installed)")
  84. return nil
  85. }
  86. func (e *ESOV2) createRBAC() error {
  87. // Create ServiceAccount
  88. sa := &appsv1.ServiceAccount{
  89. ObjectMeta: metav1.ObjectMeta{
  90. Name: v2ControllerName,
  91. Namespace: v2Namespace,
  92. },
  93. }
  94. _, err := e.kubeClientSet.CoreV1().ServiceAccounts(v2Namespace).Create(context.Background(), sa, metav1.CreateOptions{})
  95. if err != nil && !isAlreadyExists(err) {
  96. return err
  97. }
  98. // Create ClusterRole
  99. clusterRole := &rbacv1.ClusterRole{
  100. ObjectMeta: metav1.ObjectMeta{
  101. Name: v2ControllerName,
  102. },
  103. Rules: []rbacv1.PolicyRule{
  104. {
  105. APIGroups: []string{"external-secrets.io"},
  106. Resources: []string{"secretstores", "clustersecretstores", "externalsecrets"},
  107. Verbs: []string{"get", "list", "watch"},
  108. },
  109. {
  110. APIGroups: []string{"external-secrets.io"},
  111. Resources: []string{"secretstores/status", "clustersecretstores/status", "externalsecrets/status"},
  112. Verbs: []string{"get", "patch", "update"},
  113. },
  114. {
  115. APIGroups: []string{""},
  116. Resources: []string{"secrets"},
  117. Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
  118. },
  119. {
  120. APIGroups: []string{""},
  121. Resources: []string{"events"},
  122. Verbs: []string{"create", "patch"},
  123. },
  124. },
  125. }
  126. _, err = e.kubeClientSet.RbacV1().ClusterRoles().Create(context.Background(), clusterRole, metav1.CreateOptions{})
  127. if err != nil && !isAlreadyExists(err) {
  128. return err
  129. }
  130. // Create ClusterRoleBinding
  131. clusterRoleBinding := &rbacv1.ClusterRoleBinding{
  132. ObjectMeta: metav1.ObjectMeta{
  133. Name: v2ControllerName,
  134. },
  135. Subjects: []rbacv1.Subject{
  136. {
  137. Kind: "ServiceAccount",
  138. Name: v2ControllerName,
  139. Namespace: v2Namespace,
  140. },
  141. },
  142. RoleRef: rbacv1.RoleRef{
  143. APIGroup: "rbac.authorization.k8s.io",
  144. Kind: "ClusterRole",
  145. Name: v2ControllerName,
  146. },
  147. }
  148. _, err = e.kubeClientSet.RbacV1().ClusterRoleBindings().Create(context.Background(), clusterRoleBinding, metav1.CreateOptions{})
  149. if err != nil && !isAlreadyExists(err) {
  150. return err
  151. }
  152. // Create ServiceAccount for provider
  153. providerSA := &appsv1.ServiceAccount{
  154. ObjectMeta: metav1.ObjectMeta{
  155. Name: v2ProviderName,
  156. Namespace: v2Namespace,
  157. },
  158. }
  159. _, err = e.kubeClientSet.CoreV1().ServiceAccounts(v2Namespace).Create(context.Background(), providerSA, metav1.CreateOptions{})
  160. if err != nil && !isAlreadyExists(err) {
  161. return err
  162. }
  163. // Create ClusterRole for provider
  164. providerClusterRole := &rbacv1.ClusterRole{
  165. ObjectMeta: metav1.ObjectMeta{
  166. Name: v2ProviderName,
  167. },
  168. Rules: []rbacv1.PolicyRule{
  169. {
  170. APIGroups: []string{""},
  171. Resources: []string{"secrets"},
  172. Verbs: []string{"get", "list"},
  173. },
  174. },
  175. }
  176. _, err = e.kubeClientSet.RbacV1().ClusterRoles().Create(context.Background(), providerClusterRole, metav1.CreateOptions{})
  177. if err != nil && !isAlreadyExists(err) {
  178. return err
  179. }
  180. // Create ClusterRoleBinding for provider
  181. providerClusterRoleBinding := &rbacv1.ClusterRoleBinding{
  182. ObjectMeta: metav1.ObjectMeta{
  183. Name: v2ProviderName,
  184. },
  185. Subjects: []rbacv1.Subject{
  186. {
  187. Kind: "ServiceAccount",
  188. Name: v2ProviderName,
  189. Namespace: v2Namespace,
  190. },
  191. },
  192. RoleRef: rbacv1.RoleRef{
  193. APIGroup: "rbac.authorization.k8s.io",
  194. Kind: "ClusterRole",
  195. Name: v2ProviderName,
  196. },
  197. }
  198. _, err = e.kubeClientSet.RbacV1().ClusterRoleBindings().Create(context.Background(), providerClusterRoleBinding, metav1.CreateOptions{})
  199. if err != nil && !isAlreadyExists(err) {
  200. return err
  201. }
  202. log.Logf("created RBAC resources")
  203. return nil
  204. }
  205. func (e *ESOV2) deployController() error {
  206. // This would deploy the actual controller
  207. // For E2E tests, we assume it's deployed via Helm or manifests
  208. log.Logf("controller deployment (assuming pre-deployed)")
  209. return nil
  210. }
  211. func (e *ESOV2) deployKubernetesProvider() error {
  212. // This would deploy the Kubernetes provider
  213. // For E2E tests, we assume it's deployed via Helm or manifests
  214. log.Logf("Kubernetes provider deployment (assuming pre-deployed)")
  215. return nil
  216. }
  217. func (e *ESOV2) waitForDeployments() error {
  218. log.Logf("waiting for deployments to be ready")
  219. ctx := context.Background()
  220. // Wait for controller deployment
  221. err := wait.PollImmediate(5*time.Second, 5*time.Minute, func() (bool, error) {
  222. deployment, err := e.kubeClientSet.AppsV1().Deployments(v2Namespace).Get(ctx, v2ControllerName, metav1.GetOptions{})
  223. if err != nil {
  224. log.Logf("waiting for controller deployment: %v", err)
  225. return false, nil
  226. }
  227. if deployment.Status.ReadyReplicas == deployment.Status.Replicas && deployment.Status.Replicas > 0 {
  228. log.Logf("controller deployment is ready")
  229. return true, nil
  230. }
  231. log.Logf("controller deployment not ready yet: %d/%d replicas", deployment.Status.ReadyReplicas, deployment.Status.Replicas)
  232. return false, nil
  233. })
  234. if err != nil {
  235. return fmt.Errorf("controller deployment not ready: %w", err)
  236. }
  237. // Wait for provider deployment
  238. err = wait.PollImmediate(5*time.Second, 5*time.Minute, func() (bool, error) {
  239. deployment, err := e.kubeClientSet.AppsV1().Deployments(v2Namespace).Get(ctx, v2ProviderName, metav1.GetOptions{})
  240. if err != nil {
  241. log.Logf("waiting for provider deployment: %v", err)
  242. return false, nil
  243. }
  244. if deployment.Status.ReadyReplicas == deployment.Status.Replicas && deployment.Status.Replicas > 0 {
  245. log.Logf("provider deployment is ready")
  246. return true, nil
  247. }
  248. log.Logf("provider deployment not ready yet: %d/%d replicas", deployment.Status.ReadyReplicas, deployment.Status.Replicas)
  249. return false, nil
  250. })
  251. if err != nil {
  252. return fmt.Errorf("provider deployment not ready: %w", err)
  253. }
  254. return nil
  255. }
  256. // Logs returns the logs of the ESO V2 components.
  257. func (e *ESOV2) Logs() error {
  258. log.Logf("=== Controller Logs ===")
  259. if err := printPodLogs(e.kubeClientSet, v2Namespace, "app="+v2ControllerName); err != nil {
  260. log.Logf("failed to get controller logs: %v", err)
  261. }
  262. log.Logf("=== Provider Logs ===")
  263. if err := printPodLogs(e.kubeClientSet, v2Namespace, "app="+v2ProviderName); err != nil {
  264. log.Logf("failed to get provider logs: %v", err)
  265. }
  266. return nil
  267. }
  268. // Uninstall removes ESO V2 components.
  269. func (e *ESOV2) Uninstall() error {
  270. log.Logf("uninstalling External Secrets Operator V2")
  271. ctx := context.Background()
  272. // Delete deployments
  273. _ = e.kubeClientSet.AppsV1().Deployments(v2Namespace).Delete(ctx, v2ControllerName, metav1.DeleteOptions{})
  274. _ = e.kubeClientSet.AppsV1().Deployments(v2Namespace).Delete(ctx, v2ProviderName, metav1.DeleteOptions{})
  275. // Delete RBAC
  276. _ = e.kubeClientSet.RbacV1().ClusterRoleBindings().Delete(ctx, v2ControllerName, metav1.DeleteOptions{})
  277. _ = e.kubeClientSet.RbacV1().ClusterRoles().Delete(ctx, v2ControllerName, metav1.DeleteOptions{})
  278. _ = e.kubeClientSet.RbacV1().ClusterRoleBindings().Delete(ctx, v2ProviderName, metav1.DeleteOptions{})
  279. _ = e.kubeClientSet.RbacV1().ClusterRoles().Delete(ctx, v2ProviderName, metav1.DeleteOptions{})
  280. _ = e.kubeClientSet.CoreV1().ServiceAccounts(v2Namespace).Delete(ctx, v2ControllerName, metav1.DeleteOptions{})
  281. _ = e.kubeClientSet.CoreV1().ServiceAccounts(v2Namespace).Delete(ctx, v2ProviderName, metav1.DeleteOptions{})
  282. // Delete namespace
  283. _ = e.kubeClientSet.CoreV1().Namespaces().Delete(ctx, v2Namespace, metav1.DeleteOptions{})
  284. log.Logf("External Secrets Operator V2 uninstalled")
  285. return nil
  286. }
  287. func isAlreadyExists(err error) bool {
  288. return err != nil && (err.Error() == "already exists" || errors.IsAlreadyExists(err))
  289. }
  290. func printPodLogs(clientset kubernetes.Interface, namespace, labelSelector string) error {
  291. ctx := context.Background()
  292. pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
  293. LabelSelector: labelSelector,
  294. })
  295. if err != nil {
  296. return err
  297. }
  298. for _, pod := range pods.Items {
  299. log.Logf("Logs for pod %s:", pod.Name)
  300. req := clientset.CoreV1().Pods(namespace).GetLogs(pod.Name, &appsv1.PodLogOptions{})
  301. logs, err := req.Stream(ctx)
  302. if err != nil {
  303. log.Logf("failed to get logs: %v", err)
  304. continue
  305. }
  306. defer logs.Close()
  307. buf := make([]byte, 2048)
  308. for {
  309. n, err := logs.Read(buf)
  310. if n > 0 {
  311. log.Logf("%s", string(buf[:n]))
  312. }
  313. if err != nil {
  314. break
  315. }
  316. }
  317. }
  318. return nil
  319. }