package e2e import ( "context" "fmt" "strings" . "github.com/onsi/gomega" // nolint metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" e2elog "k8s.io/kubernetes/test/e2e/framework/log" ) var ( vaultExamplePath = "../examples/kms/vault/" vaultServicePath = "vault.yaml" vaultPSPPath = "vault-psp.yaml" vaultRBACPath = "csi-vaulttokenreview-rbac.yaml" vaultConfigPath = "kms-config.yaml" vaultTenantPath = "tenant-sa.yaml" vaultTenantAdminPath = "tenant-sa-admin.yaml" ) func deployVault(c kubernetes.Interface, deployTimeout int) { // hack to make helm E2E pass as helm charts creates this configmap as part // of cephcsi deployment err := retryKubectlArgs( cephCSINamespace, kubectlDelete, deployTimeout, "cm", "ceph-csi-encryption-kms-config", "--ignore-not-found=true") Expect(err).Should(BeNil()) createORDeleteVault(kubectlCreate) opt := metav1.ListOptions{ LabelSelector: "app=vault", } pods, err := c.CoreV1().Pods(cephCSINamespace).List(context.TODO(), opt) Expect(err).Should(BeNil()) Expect(len(pods.Items)).Should(Equal(1)) name := pods.Items[0].Name err = waitForPodInRunningState(name, cephCSINamespace, c, deployTimeout, noError) Expect(err).Should(BeNil()) } func deleteVault() { createORDeleteVault(kubectlDelete) } func createORDeleteVault(action kubectlAction) { data, err := replaceNamespaceInTemplate(vaultExamplePath + vaultServicePath) if err != nil { e2elog.Failf("failed to read content from %s %v", vaultExamplePath+vaultServicePath, err) } data = strings.ReplaceAll(data, "vault.default", "vault."+cephCSINamespace) data = strings.ReplaceAll(data, "value: default", "value: "+cephCSINamespace) err = retryKubectlInput(cephCSINamespace, action, data, deployTimeout) if err != nil { e2elog.Failf("failed to %s vault statefulset %v", action, err) } data, err = replaceNamespaceInTemplate(vaultExamplePath + vaultRBACPath) if err != nil { e2elog.Failf("failed to read content from %s %v", vaultExamplePath+vaultRBACPath, err) } err = retryKubectlInput(cephCSINamespace, action, data, deployTimeout) if err != nil { e2elog.Failf("failed to %s vault statefulset %v", action, err) } data, err = replaceNamespaceInTemplate(vaultExamplePath + vaultConfigPath) if err != nil { e2elog.Failf("failed to read content from %s %v", vaultExamplePath+vaultConfigPath, err) } data = strings.ReplaceAll(data, "default", cephCSINamespace) err = retryKubectlInput(cephCSINamespace, action, data, deployTimeout) if err != nil { e2elog.Failf("failed to %s vault configmap %v", action, err) } data, err = replaceNamespaceInTemplate(vaultExamplePath + vaultPSPPath) if err != nil { e2elog.Failf("failed to read content from %s %v", vaultExamplePath+vaultPSPPath, err) } err = retryKubectlInput(cephCSINamespace, action, data, deployTimeout) if err != nil { e2elog.Failf("failed to %s vault psp %v", action, err) } } // createTenantServiceAccount uses the tenant-sa.yaml example file to create // the ServiceAccount for the tenant and configured Hashicorp Vault with a // kv-store that the ServiceAccount has access to. func createTenantServiceAccount(c kubernetes.Interface, ns string) error { err := createORDeleteTenantServiceAccount(kubectlCreate, ns) if err != nil { return fmt.Errorf("failed to create ServiceAccount: %w", err) } // wait for the Job to finish const jobName = "vault-tenant-sa" err = waitForJobCompletion(c, cephCSINamespace, jobName, deployTimeout) if err != nil { return fmt.Errorf("job %s/%s did not succeed: %w", cephCSINamespace, jobName, err) } return nil } // deleteTenantServiceAccount removed the ServiceAccount and other objects that // were created with createTenantServiceAccount. func deleteTenantServiceAccount(ns string) { err := createORDeleteTenantServiceAccount(kubectlDelete, ns) Expect(err).Should(BeNil()) } // createORDeleteTenantServiceAccount is a helper that reads the tenant-sa.yaml // example file and replaces the default namespaces with the current deployment // configuration. func createORDeleteTenantServiceAccount(action kubectlAction, ns string) error { err := retryKubectlFile(ns, action, vaultExamplePath+vaultTenantPath, deployTimeout) if err != nil { return fmt.Errorf("failed to %s tenant ServiceAccount: %w", action, err) } // the ServiceAccount needs permissions in Vault, the admin job sets that up data, err := replaceNamespaceInTemplate(vaultExamplePath + vaultTenantAdminPath) if err != nil { return fmt.Errorf("failed to read content from %q: %w", vaultExamplePath+vaultTenantAdminPath, err) } // replace the value for TENANT_NAMESPACE data = strings.ReplaceAll(data, "value: tenant", "value: "+ns) // replace "default" in the URL to the Vault service data = strings.ReplaceAll(data, "vault.default", "vault."+cephCSINamespace) err = retryKubectlInput(cephCSINamespace, action, data, deployTimeout) if err != nil { return fmt.Errorf("failed to %s ServiceAccount: %w", action, err) } return nil }