|
|
@@ -1,372 +0,0 @@
|
|
|
-/*
|
|
|
-Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
-you may not use this file except in compliance with the License.
|
|
|
-You may obtain a copy of the License at
|
|
|
-
|
|
|
- http://www.apache.org/licenses/LICENSE-2.0
|
|
|
-
|
|
|
-Unless required by applicable law or agreed to in writing, software
|
|
|
-distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
-See the License for the specific language governing permissions and
|
|
|
-limitations under the License.
|
|
|
-*/
|
|
|
-
|
|
|
-package addon
|
|
|
-
|
|
|
-import (
|
|
|
- "context"
|
|
|
- "fmt"
|
|
|
- "time"
|
|
|
-
|
|
|
- "github.com/external-secrets/external-secrets-e2e/framework/log"
|
|
|
- appsv1 "k8s.io/api/core/v1"
|
|
|
- rbacv1 "k8s.io/api/rbac/v1"
|
|
|
- "k8s.io/apimachinery/pkg/api/errors"
|
|
|
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
- "k8s.io/apimachinery/pkg/util/wait"
|
|
|
- "k8s.io/client-go/kubernetes"
|
|
|
-)
|
|
|
-
|
|
|
-const (
|
|
|
- v2Namespace = "external-secrets-system"
|
|
|
- v2ControllerName = "external-secrets-v2"
|
|
|
- v2ProviderName = "kubernetes-provider"
|
|
|
-)
|
|
|
-
|
|
|
-// ESOV2 is an addon that installs External Secrets Operator V2 with Kubernetes provider.
|
|
|
-type ESOV2 struct {
|
|
|
- config *Config
|
|
|
- kubeClientSet kubernetes.Interface
|
|
|
-}
|
|
|
-
|
|
|
-// Setup installs ESO V2 controller and Kubernetes provider.
|
|
|
-func (e *ESOV2) Setup(config *Config) error {
|
|
|
- e.config = config
|
|
|
- e.kubeClientSet = config.KubeClientSet
|
|
|
-
|
|
|
- log.Logf("installing External Secrets Operator V2")
|
|
|
-
|
|
|
- // Create namespace
|
|
|
- if err := e.createNamespace(); err != nil {
|
|
|
- return fmt.Errorf("failed to create namespace: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- // Install CRDs
|
|
|
- if err := e.installCRDs(); err != nil {
|
|
|
- return fmt.Errorf("failed to install CRDs: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- // Create RBAC
|
|
|
- if err := e.createRBAC(); err != nil {
|
|
|
- return fmt.Errorf("failed to create RBAC: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- // Deploy controller
|
|
|
- if err := e.deployController(); err != nil {
|
|
|
- return fmt.Errorf("failed to deploy controller: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- // Deploy Kubernetes provider
|
|
|
- if err := e.deployKubernetesProvider(); err != nil {
|
|
|
- return fmt.Errorf("failed to deploy Kubernetes provider: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- // Wait for deployments to be ready
|
|
|
- if err := e.waitForDeployments(); err != nil {
|
|
|
- return fmt.Errorf("failed waiting for deployments: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- log.Logf("External Secrets Operator V2 installed successfully")
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (e *ESOV2) createNamespace() error {
|
|
|
- ns := &appsv1.Namespace{
|
|
|
- ObjectMeta: metav1.ObjectMeta{
|
|
|
- Name: v2Namespace,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- _, err := e.kubeClientSet.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{})
|
|
|
- if err != nil && !isAlreadyExists(err) {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- log.Logf("created namespace: %s", v2Namespace)
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (e *ESOV2) installCRDs() error {
|
|
|
- // In a real implementation, this would apply actual CRD manifests
|
|
|
- // For now, we'll assume CRDs are already installed or use the Helm chart
|
|
|
- log.Logf("CRDs installation (assuming pre-installed)")
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (e *ESOV2) createRBAC() error {
|
|
|
- // Create ServiceAccount
|
|
|
- sa := &appsv1.ServiceAccount{
|
|
|
- ObjectMeta: metav1.ObjectMeta{
|
|
|
- Name: v2ControllerName,
|
|
|
- Namespace: v2Namespace,
|
|
|
- },
|
|
|
- }
|
|
|
- _, err := e.kubeClientSet.CoreV1().ServiceAccounts(v2Namespace).Create(context.Background(), sa, metav1.CreateOptions{})
|
|
|
- if err != nil && !isAlreadyExists(err) {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // Create ClusterRole
|
|
|
- clusterRole := &rbacv1.ClusterRole{
|
|
|
- ObjectMeta: metav1.ObjectMeta{
|
|
|
- Name: v2ControllerName,
|
|
|
- },
|
|
|
- Rules: []rbacv1.PolicyRule{
|
|
|
- {
|
|
|
- APIGroups: []string{"external-secrets.io"},
|
|
|
- Resources: []string{"secretstores", "clustersecretstores", "externalsecrets"},
|
|
|
- Verbs: []string{"get", "list", "watch"},
|
|
|
- },
|
|
|
- {
|
|
|
- APIGroups: []string{"external-secrets.io"},
|
|
|
- Resources: []string{"secretstores/status", "clustersecretstores/status", "externalsecrets/status"},
|
|
|
- Verbs: []string{"get", "patch", "update"},
|
|
|
- },
|
|
|
- {
|
|
|
- APIGroups: []string{""},
|
|
|
- Resources: []string{"secrets"},
|
|
|
- Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
|
|
|
- },
|
|
|
- {
|
|
|
- APIGroups: []string{""},
|
|
|
- Resources: []string{"events"},
|
|
|
- Verbs: []string{"create", "patch"},
|
|
|
- },
|
|
|
- },
|
|
|
- }
|
|
|
- _, err = e.kubeClientSet.RbacV1().ClusterRoles().Create(context.Background(), clusterRole, metav1.CreateOptions{})
|
|
|
- if err != nil && !isAlreadyExists(err) {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // Create ClusterRoleBinding
|
|
|
- clusterRoleBinding := &rbacv1.ClusterRoleBinding{
|
|
|
- ObjectMeta: metav1.ObjectMeta{
|
|
|
- Name: v2ControllerName,
|
|
|
- },
|
|
|
- Subjects: []rbacv1.Subject{
|
|
|
- {
|
|
|
- Kind: "ServiceAccount",
|
|
|
- Name: v2ControllerName,
|
|
|
- Namespace: v2Namespace,
|
|
|
- },
|
|
|
- },
|
|
|
- RoleRef: rbacv1.RoleRef{
|
|
|
- APIGroup: "rbac.authorization.k8s.io",
|
|
|
- Kind: "ClusterRole",
|
|
|
- Name: v2ControllerName,
|
|
|
- },
|
|
|
- }
|
|
|
- _, err = e.kubeClientSet.RbacV1().ClusterRoleBindings().Create(context.Background(), clusterRoleBinding, metav1.CreateOptions{})
|
|
|
- if err != nil && !isAlreadyExists(err) {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // Create ServiceAccount for provider
|
|
|
- providerSA := &appsv1.ServiceAccount{
|
|
|
- ObjectMeta: metav1.ObjectMeta{
|
|
|
- Name: v2ProviderName,
|
|
|
- Namespace: v2Namespace,
|
|
|
- },
|
|
|
- }
|
|
|
- _, err = e.kubeClientSet.CoreV1().ServiceAccounts(v2Namespace).Create(context.Background(), providerSA, metav1.CreateOptions{})
|
|
|
- if err != nil && !isAlreadyExists(err) {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // Create ClusterRole for provider
|
|
|
- providerClusterRole := &rbacv1.ClusterRole{
|
|
|
- ObjectMeta: metav1.ObjectMeta{
|
|
|
- Name: v2ProviderName,
|
|
|
- },
|
|
|
- Rules: []rbacv1.PolicyRule{
|
|
|
- {
|
|
|
- APIGroups: []string{""},
|
|
|
- Resources: []string{"secrets"},
|
|
|
- Verbs: []string{"get", "list"},
|
|
|
- },
|
|
|
- },
|
|
|
- }
|
|
|
- _, err = e.kubeClientSet.RbacV1().ClusterRoles().Create(context.Background(), providerClusterRole, metav1.CreateOptions{})
|
|
|
- if err != nil && !isAlreadyExists(err) {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // Create ClusterRoleBinding for provider
|
|
|
- providerClusterRoleBinding := &rbacv1.ClusterRoleBinding{
|
|
|
- ObjectMeta: metav1.ObjectMeta{
|
|
|
- Name: v2ProviderName,
|
|
|
- },
|
|
|
- Subjects: []rbacv1.Subject{
|
|
|
- {
|
|
|
- Kind: "ServiceAccount",
|
|
|
- Name: v2ProviderName,
|
|
|
- Namespace: v2Namespace,
|
|
|
- },
|
|
|
- },
|
|
|
- RoleRef: rbacv1.RoleRef{
|
|
|
- APIGroup: "rbac.authorization.k8s.io",
|
|
|
- Kind: "ClusterRole",
|
|
|
- Name: v2ProviderName,
|
|
|
- },
|
|
|
- }
|
|
|
- _, err = e.kubeClientSet.RbacV1().ClusterRoleBindings().Create(context.Background(), providerClusterRoleBinding, metav1.CreateOptions{})
|
|
|
- if err != nil && !isAlreadyExists(err) {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- log.Logf("created RBAC resources")
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (e *ESOV2) deployController() error {
|
|
|
- // This would deploy the actual controller
|
|
|
- // For E2E tests, we assume it's deployed via Helm or manifests
|
|
|
- log.Logf("controller deployment (assuming pre-deployed)")
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (e *ESOV2) deployKubernetesProvider() error {
|
|
|
- // This would deploy the Kubernetes provider
|
|
|
- // For E2E tests, we assume it's deployed via Helm or manifests
|
|
|
- log.Logf("Kubernetes provider deployment (assuming pre-deployed)")
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (e *ESOV2) waitForDeployments() error {
|
|
|
- log.Logf("waiting for deployments to be ready")
|
|
|
-
|
|
|
- ctx := context.Background()
|
|
|
-
|
|
|
- // Wait for controller deployment
|
|
|
- err := wait.PollImmediate(5*time.Second, 5*time.Minute, func() (bool, error) {
|
|
|
- deployment, err := e.kubeClientSet.AppsV1().Deployments(v2Namespace).Get(ctx, v2ControllerName, metav1.GetOptions{})
|
|
|
- if err != nil {
|
|
|
- log.Logf("waiting for controller deployment: %v", err)
|
|
|
- return false, nil
|
|
|
- }
|
|
|
-
|
|
|
- if deployment.Status.ReadyReplicas == deployment.Status.Replicas && deployment.Status.Replicas > 0 {
|
|
|
- log.Logf("controller deployment is ready")
|
|
|
- return true, nil
|
|
|
- }
|
|
|
-
|
|
|
- log.Logf("controller deployment not ready yet: %d/%d replicas", deployment.Status.ReadyReplicas, deployment.Status.Replicas)
|
|
|
- return false, nil
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
- return fmt.Errorf("controller deployment not ready: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- // Wait for provider deployment
|
|
|
- err = wait.PollImmediate(5*time.Second, 5*time.Minute, func() (bool, error) {
|
|
|
- deployment, err := e.kubeClientSet.AppsV1().Deployments(v2Namespace).Get(ctx, v2ProviderName, metav1.GetOptions{})
|
|
|
- if err != nil {
|
|
|
- log.Logf("waiting for provider deployment: %v", err)
|
|
|
- return false, nil
|
|
|
- }
|
|
|
-
|
|
|
- if deployment.Status.ReadyReplicas == deployment.Status.Replicas && deployment.Status.Replicas > 0 {
|
|
|
- log.Logf("provider deployment is ready")
|
|
|
- return true, nil
|
|
|
- }
|
|
|
-
|
|
|
- log.Logf("provider deployment not ready yet: %d/%d replicas", deployment.Status.ReadyReplicas, deployment.Status.Replicas)
|
|
|
- return false, nil
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
- return fmt.Errorf("provider deployment not ready: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-// Logs returns the logs of the ESO V2 components.
|
|
|
-func (e *ESOV2) Logs() error {
|
|
|
- log.Logf("=== Controller Logs ===")
|
|
|
- if err := printPodLogs(e.kubeClientSet, v2Namespace, "app="+v2ControllerName); err != nil {
|
|
|
- log.Logf("failed to get controller logs: %v", err)
|
|
|
- }
|
|
|
-
|
|
|
- log.Logf("=== Provider Logs ===")
|
|
|
- if err := printPodLogs(e.kubeClientSet, v2Namespace, "app="+v2ProviderName); err != nil {
|
|
|
- log.Logf("failed to get provider logs: %v", err)
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-// Uninstall removes ESO V2 components.
|
|
|
-func (e *ESOV2) Uninstall() error {
|
|
|
- log.Logf("uninstalling External Secrets Operator V2")
|
|
|
-
|
|
|
- ctx := context.Background()
|
|
|
-
|
|
|
- // Delete deployments
|
|
|
- _ = e.kubeClientSet.AppsV1().Deployments(v2Namespace).Delete(ctx, v2ControllerName, metav1.DeleteOptions{})
|
|
|
- _ = e.kubeClientSet.AppsV1().Deployments(v2Namespace).Delete(ctx, v2ProviderName, metav1.DeleteOptions{})
|
|
|
-
|
|
|
- // Delete RBAC
|
|
|
- _ = e.kubeClientSet.RbacV1().ClusterRoleBindings().Delete(ctx, v2ControllerName, metav1.DeleteOptions{})
|
|
|
- _ = e.kubeClientSet.RbacV1().ClusterRoles().Delete(ctx, v2ControllerName, metav1.DeleteOptions{})
|
|
|
- _ = e.kubeClientSet.RbacV1().ClusterRoleBindings().Delete(ctx, v2ProviderName, metav1.DeleteOptions{})
|
|
|
- _ = e.kubeClientSet.RbacV1().ClusterRoles().Delete(ctx, v2ProviderName, metav1.DeleteOptions{})
|
|
|
- _ = e.kubeClientSet.CoreV1().ServiceAccounts(v2Namespace).Delete(ctx, v2ControllerName, metav1.DeleteOptions{})
|
|
|
- _ = e.kubeClientSet.CoreV1().ServiceAccounts(v2Namespace).Delete(ctx, v2ProviderName, metav1.DeleteOptions{})
|
|
|
-
|
|
|
- // Delete namespace
|
|
|
- _ = e.kubeClientSet.CoreV1().Namespaces().Delete(ctx, v2Namespace, metav1.DeleteOptions{})
|
|
|
-
|
|
|
- log.Logf("External Secrets Operator V2 uninstalled")
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func isAlreadyExists(err error) bool {
|
|
|
- return err != nil && (err.Error() == "already exists" || errors.IsAlreadyExists(err))
|
|
|
-}
|
|
|
-
|
|
|
-func printPodLogs(clientset kubernetes.Interface, namespace, labelSelector string) error {
|
|
|
- ctx := context.Background()
|
|
|
-
|
|
|
- pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
|
|
|
- LabelSelector: labelSelector,
|
|
|
- })
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- for _, pod := range pods.Items {
|
|
|
- log.Logf("Logs for pod %s:", pod.Name)
|
|
|
- req := clientset.CoreV1().Pods(namespace).GetLogs(pod.Name, &appsv1.PodLogOptions{})
|
|
|
- logs, err := req.Stream(ctx)
|
|
|
- if err != nil {
|
|
|
- log.Logf("failed to get logs: %v", err)
|
|
|
- continue
|
|
|
- }
|
|
|
- defer logs.Close()
|
|
|
-
|
|
|
- buf := make([]byte, 2048)
|
|
|
- for {
|
|
|
- n, err := logs.Read(buf)
|
|
|
- if n > 0 {
|
|
|
- log.Logf("%s", string(buf[:n]))
|
|
|
- }
|
|
|
- if err != nil {
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|