diff --git a/plugins/contrib/pre-deploy/02-import-secrets.js b/plugins/contrib/pre-deploy/02-import-secrets.js index 2709165a9..682e76a05 100644 --- a/plugins/contrib/pre-deploy/02-import-secrets.js +++ b/plugins/contrib/pre-deploy/02-import-secrets.js @@ -1,5 +1,91 @@ const async = require("async") +function getNeededSecretNames(manifests) { + const secretSet = new Set() + + manifests.forEach((manifest) => { + if ( + ["Deployment", "StatefulSet", "CronJob", "Job", "DaemonSet"].includes( + manifest.kind + ) + ) { + const { spec } = manifest + + // For CronJob, we need to go one level deeper + const targetSpec = + manifest.kind === "CronJob" ? spec.jobTemplate.spec : spec + + // Check for secrets in environment variables + const containers = targetSpec.template.spec.containers || [] + containers.forEach((container) => { + const envFrom = container.envFrom || [] + envFrom.forEach((env) => { + if (env.secretRef && env.secretRef.name) { + secretSet.add(env.secretRef.name) + } + }) + + const env = container.env || [] + env.forEach((envVar) => { + if (envVar.valueFrom && envVar.valueFrom.secretKeyRef) { + secretSet.add(envVar.valueFrom.secretKeyRef.name) + } + }) + }) + + // Check for secrets in volumes + const volumes = targetSpec.template.spec.volumes || [] + volumes.forEach((volume) => { + if (volume.secret && volume.secret.secretName) { + secretSet.add(volume.secret.secretName) + } + }) + } + }) + + return Array.from(secretSet) +} + +function filterOutExistingSecrets(manifests, secretNames) { + const existingSecrets = new Set() + + manifests.forEach((manifest) => { + if (manifest.kind === "Secret") { + if (manifest.metadata && manifest.metadata.name) { + existingSecrets.add(manifest.metadata.name) + } + } else if (manifest.kind === "SealedSecret") { + // SealedSecret uses spec.template.metadata.name + if ( + manifest.spec && + manifest.spec.template && + manifest.spec.template.metadata && + manifest.spec.template.metadata.name + ) { + existingSecrets.add(manifest.spec.template.metadata.name) + } + } + }) + + return secretNames.filter((secretName) => !existingSecrets.has(secretName)) +} + +async function getSecretNamesFromCiNamespace( + ciNamespace, + kubectl, + kubectlOptions +) { + const names = await kubectl(`get -n ${ciNamespace} secret -oname`, { + ...kubectlOptions, + logInfo: false, + logError: false, + }) + return names + .split("\n") + .map((name) => name.trim().split("/").pop()) + .filter((name) => name) +} + module.exports = async (manifests, options, context) => { const { utils, config, logger, kubectl } = context const { KontinuousPluginError } = utils @@ -18,7 +104,34 @@ module.exports = async (manifests, options, context) => { surviveOnBrokenCluster, } - const { secrets } = options + const { copyAllNeeded = false, copyAllFromCiNamespace = false } = options + let { secrets } = options + + if (copyAllNeeded) { + const neededSecretNames = getNeededSecretNames(manifests) + const listedSecretNames = filterOutExistingSecrets( + manifests, + neededSecretNames + ) + + secrets = { + ...listedSecretNames.reduce((acc, name) => ({ ...acc, [name]: {} }), {}), + ...secrets, + } + } + + if (copyAllFromCiNamespace) { + const listedSecretNames = await getSecretNamesFromCiNamespace( + ciNamespace, + kubectl, + kubectlOptions + ) + + secrets = { + ...listedSecretNames.reduce((acc, name) => ({ ...acc, [name]: {} }), {}), + ...secrets, + } + } const importSecretExec = async (secret) => { const { diff --git a/plugins/fabrique/kontinuous.yaml b/plugins/fabrique/kontinuous.yaml index 619abbff6..3bc14b727 100644 --- a/plugins/fabrique/kontinuous.yaml +++ b/plugins/fabrique/kontinuous.yaml @@ -103,25 +103,11 @@ dependencies: preDeploy: importSecrets: - enabled: false + enabled: true options: - secrets: - kubeconfig: - harbor: - buildkit-client-certs: - pg-admin-user: - from: - - azure-pg-admin-user - - pg-scaleway - # secret-name: - # enabled: true - # reload: false - # required: false - # fromNamespace: <$projectName-ci> - # toNamespace: true - # toAllNamespace: false - # to: azure-pg-admin-user - # from: [azure-pg-admin-user] + copyAllNeeded: false + copyAllFromCiNamespace: true + secrets: {} rancherNamespaces: enabled: true cleanFailed: