UNPKG

@facets-cloud/facetsctl

Version:
245 lines (244 loc) 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@oclif/core"); const url = require("url"); const configuration_1 = require("../utils/configuration"); const FacetsAPI = require("../services/facets-api"); const Listr = require("listr"); const errors_1 = require("@oclif/core/lib/errors"); const { promisify } = require("util"); const exec = promisify(require("child_process").exec); var RegistrationTypes; (function (RegistrationTypes) { RegistrationTypes["CLUSTER"] = "CLUSTER"; RegistrationTypes["RELEASE_STREAM"] = "RELEASE_STREAM"; })(RegistrationTypes || (RegistrationTypes = {})); class ArtifactPush extends core_1.Command { constructor() { super(...arguments); this.showOriginalError = false; this.checkUserCredentials = (ctx) => { try { const config = (0, configuration_1.readConfig)(); ctx.config = config; } catch (error) { if (!error.message) { error.message = "Error while fetching user credentials! Make sure you are logged in before pushing"; } throw new errors_1.CLIError(error, { exit: 2 }); } }; this.fetchRepoCredentials = async (ctx) => { try { const config = ctx.config; const { flags } = await this.parse(ArtifactPush); let result; if (flags["git-ref"]) { result = await FacetsAPI.fetchPushCredentialsByBranch(config.ControlPlaneURL, config.Username, config.AccessToken, flags["artifact-name"], flags["git-ref"]); } else if (flags["registration-type"] && flags["registration-value"]) { result = await FacetsAPI.fetchPushCredentials(config.ControlPlaneURL, config.Username, config.AccessToken, flags["artifact-name"], flags["registration-type"], flags["registration-value"]); } const { data } = result || {}; ctx.authorizationToken = data.authorizationToken; ctx.proxyEndpoint = data.proxyEndpoint; } catch (error) { if (this.showOriginalError) { throw new errors_1.CLIError(error, { exit: 2 }); } if (error.code === "ENOTFOUND") { error.message = "Incorrect URL of Control Plane!"; } if (error.code === "ERR_BAD_REQUEST") { error.message = "Unauthorized access!"; } throw new errors_1.CLIError(error, { exit: 2 }); } }; this.registerArtifact = async (ctx) => { try { const { flags } = await this.parse(ArtifactPush); const config = ctx.config; const artifact = { applicationName: flags["artifact-name"], artifactUri: ctx.imageURI, artifactory: flags.artifactory || "default", description: flags.description || "", externalId: flags["external-id"] || "", }; if (flags["git-ref"]) { artifact.metadata = { BRANCH_NAME: flags["git-ref"], }; } else { artifact.tag = ctx.tag; const registrationTypeKey = flags["registration-type"] === RegistrationTypes.CLUSTER ? "clusterId" : "releaseStream"; artifact[registrationTypeKey] = flags["registration-value"]; } flags["git-ref"] ? await FacetsAPI.pushArtifactWithMetadata(config === null || config === void 0 ? void 0 : config.ControlPlaneURL, config === null || config === void 0 ? void 0 : config.Username, config === null || config === void 0 ? void 0 : config.AccessToken, artifact) : await FacetsAPI.registerArtifact(config === null || config === void 0 ? void 0 : config.ControlPlaneURL, config === null || config === void 0 ? void 0 : config.Username, config === null || config === void 0 ? void 0 : config.AccessToken, artifact); } catch (error) { if (this.showOriginalError) { throw new errors_1.CLIError(error, { exit: 2 }); } if (error.code === "ENOTFOUND") { error.message = "Incorrect URL of Control Plane!"; } if (error.code === "ERR_BAD_REQUEST") { error.message = "Unauthorized access!"; } throw new errors_1.CLIError(error, { exit: 2 }); } }; this.pushNewImage = async (ctx) => { var _a, _b, _c, _d, _e, _f; try { const { flags } = await this.parse(ArtifactPush); const decodedToken = Buffer.from(ctx.authorizationToken, "base64").toString("binary"); const username = decodedToken.split(":")[0]; const password = decodedToken.split(":")[1]; const endpoint = url.parse(ctx.proxyEndpoint).host || ""; const source = flags["docker-image"] || ""; const tag = ((_b = (_a = source === null || source === void 0 ? void 0 : source.split(":")) === null || _a === void 0 ? void 0 : _a.slice(-1)) === null || _b === void 0 ? void 0 : _b[0]) || ""; const imageURI = flags["git-ref"] ? `${endpoint}/facets/${(_c = flags["artifact-name"]) === null || _c === void 0 ? void 0 : _c.toLowerCase()}/branch_name/${(_d = flags["git-ref"]) === null || _d === void 0 ? void 0 : _d.toLowerCase()}:${tag}` : `${endpoint}/facets/${(_e = flags["registration-type"]) === null || _e === void 0 ? void 0 : _e.toLowerCase()}/${(_f = flags["registration-value"]) === null || _f === void 0 ? void 0 : _f.toLowerCase()}/${flags["artifact-name"].toLowerCase()}:${tag}`; ctx.imageURI = imageURI; ctx.tag = tag; try { await exec(`docker tag ${source} ${imageURI}`); } catch (error) { const finalError = this.showOriginalError ? error : "Error pushing image"; throw new Error(finalError); } // docker login ${endpoint} --username ${username} --password ${password} // docker throws a warning as stderr if we use password directly like above // to prevent this, we are using the following format where password is provided like this try { await exec(`echo "${password}" | docker login ${endpoint} --username ${username} --password-stdin`); } catch (error) { const finalError = this.showOriginalError ? error : "Error with authentication, check your repository access"; throw new Error(finalError); } try { await exec(`docker push ${imageURI}`); } catch (error) { const finalError = this.showOriginalError ? error : "Error pushing image"; throw new Error(finalError); } const { stderr: logoutErr } = await exec(`docker logout ${endpoint}`); if (logoutErr) { this.log("Error while logging out", logoutErr); } } catch (error) { if (this.showOriginalError) { throw new errors_1.CLIError(error, { exit: 2 }); } if (error.code === "ECONNREFUSED") { error.message = "Unable to connect with local docker instance! Make sure docker is running on your machine"; } throw new errors_1.CLIError(error, { exit: 2 }); } }; } async run() { const { flags } = await this.parse(ArtifactPush); this.showOriginalError = flags.debug === "true"; if (!((flags["registration-type"] && flags["registration-value"]) || flags["git-ref"])) { throw new errors_1.CLIError("Either (registration-type and registration-value) or git-ref are required", { exit: 2 }); } const tasks = new Listr([ { title: "Checking user credentials", task: this.checkUserCredentials, }, { title: "Fetching repository credentials", task: this.fetchRepoCredentials, }, { title: "Pushing new image", task: this.pushNewImage, }, { title: "Registering artifact", task: this.registerArtifact, }, ]); try { await tasks.run(); } catch (error) { // Throwing the error prints a duplicate message from bin/run as well, however that is fine to go with throw error; } } } exports.default = ArtifactPush; ArtifactPush.description = "Post new docker artifacts to the facets control plane"; ArtifactPush.examples = ["<%= config.bin %> <%= command.id %>"]; ArtifactPush.flags = { "docker-image": core_1.Flags.string({ char: "i", description: "image location <IMAGE:TAG>", required: true, }), "artifact-name": core_1.Flags.string({ char: "a", description: "name of the artifact as mentioned in blueprint", required: true, }), "registration-type": core_1.Flags.string({ description: "mode of registration", options: [RegistrationTypes.CLUSTER, RegistrationTypes.RELEASE_STREAM], required: false, }), "registration-value": core_1.Flags.string({ description: "value for release stream or cluster id, based on mode of registration", required: false, }), description: core_1.Flags.string({ char: "d", description: "description of the build if any", required: false, }), "external-id": core_1.Flags.string({ char: "e", description: "external identifier for artifact", required: true, }), artifactory: core_1.Flags.string({ description: "artifactory where image will be pushed", required: false, }), debug: core_1.Flags.string({ description: "See original error message thrown", required: false, default: "false", }), "git-ref": core_1.Flags.string({ description: "Git ref provided in CI Rule", required: false, }), }; ArtifactPush.args = [];