UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

151 lines 6.23 kB
"use strict"; /* * Copyright © 2020 Atomist, Inc. * * 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. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.decryptSecret = exports.encryptSecret = exports.encodeSecret = exports.secretTemplate = exports.upsertSecrets = void 0; const logger_1 = require("@atomist/automation-client/lib/util/logger"); const _ = require("lodash"); const crypto_1 = require("../support/crypto"); const error_1 = require("../support/error"); const retry_1 = require("../support/retry"); const labels_1 = require("./labels"); const metadata_1 = require("./metadata"); const patch_1 = require("./patch"); const request_1 = require("./request"); const resource_1 = require("./resource"); /** * Create application secrets if they do not exist. If a secret in * `req.secrets` exists, the secret is patched. The provided secrets * are merged through [[secretTemplate]] before creating/patching. If * `req.secrets` is false or any empty array, no secrets are modified. * * @param req Kuberenetes application request * @return Array of secret specs created/patched, which array may be empty */ async function upsertSecrets(req) { const slug = request_1.appName(req); if (!req.secrets || req.secrets.length < 1) { logger_1.logger.debug(`No secrets provided, will not create secrets for ${slug}`); return []; } const ss = await Promise.all(req.secrets.map(async (secret) => { const secretName = `${req.ns}/${secret.metadata.name}`; const spec = await secretTemplate(req, secret); try { await req.clients.core.readNamespacedSecret(secret.metadata.name, spec.metadata.namespace); } catch (e) { logger_1.logger.debug(`Failed to read secret ${secretName}, creating: ${error_1.k8sErrMsg(e)}`); logger_1.logger.info(`Creating secret ${slug} using '${resource_1.logObject(spec)}'`); await retry_1.logRetry(() => req.clients.core.createNamespacedSecret(spec.metadata.namespace, spec), `create secret ${secretName} for ${slug}`); return spec; } logger_1.logger.info(`Secret ${secretName} exists, patching using '${resource_1.logObject(spec)}'`); await retry_1.logRetry(() => req.clients.core.patchNamespacedSecret(secret.metadata.name, spec.metadata.namespace, spec, undefined, undefined, undefined, undefined, patch_1.patchHeaders(req)), `patch secret ${secretName} for ${slug}`); return spec; })); return ss; } exports.upsertSecrets = upsertSecrets; /** * Add labels to a secret so we can delete it later. * * @param secret the unlabeled secret * @return the provided secret with appropriate labels */ async function secretTemplate(req, secret) { const labels = labels_1.applicationLabels(Object.assign(Object.assign({}, req), { component: "secret" })); const metadata = metadata_1.metadataTemplate({ namespace: req.ns, labels, }); const apiVersion = "v1"; const kind = "Secret"; const s = { type: "Opaque", metadata, }; _.merge(s, secret, { apiVersion, kind }); s.metadata.namespace = req.ns; return s; } exports.secretTemplate = secretTemplate; /** * Create encoded opaque secret object from key/value pairs. * * @param secrets Key/value pairs of secrets, the values will be base64 encoded in the returned secret * @return Kubernetes secret object */ function encodeSecret(name, data) { const metadata = metadata_1.metadataTemplate({ name }); // avoid https://github.com/kubernetes-client/javascript/issues/52 const secret = { apiVersion: "v1", kind: "Secret", type: "Opaque", metadata, data: {}, }; Object.keys(data).forEach(key => secret.data[key] = Buffer.from(data[key]).toString("base64")); return secret; } exports.encodeSecret = encodeSecret; /** * Return a copy of the provided secret with its data values * encrypted. The provided secret should have its data values base64 * encoded. The provided secret is not modified. * * @param secret Kubernetes secret with base64 encoded data values * @return Kubernetes secret object with encrypted data values */ async function encryptSecret(secret, key) { const encrypted = handleDataStrings(_.cloneDeep(secret)); for (const datum of Object.keys(encrypted.data)) { encrypted.data[datum] = await crypto_1.encrypt(encrypted.data[datum], key); } return encrypted; } exports.encryptSecret = encryptSecret; /** * Return the provided secret with any data residing in the * stringData section base64 encoded and moved into the data section. Keys in * stringData will override existing keys in data. * @param secret Kubernetes secret with stringData elements * @return Kubernetes secret with stringData elements encoded and moved to data */ function handleDataStrings(secret) { if (secret.stringData) { Object.keys(secret.stringData).forEach(key => secret.data[key] = Buffer.from(secret.stringData[key]).toString("base64")); delete secret.stringData; } return secret; } /** * Return a copy of the provided secret with its data valued * decrypted. The provided secret is not modified. * * @param secret Kubernetes secret with encrypted data values * @return Kubernetes secret object with base64 encoded data values */ async function decryptSecret(secret, key) { const decrypted = _.cloneDeep(secret); for (const datum of Object.keys(secret.data)) { decrypted.data[datum] = await crypto_1.decrypt(secret.data[datum], key); } return decrypted; } exports.decryptSecret = decryptSecret; //# sourceMappingURL=secret.js.map