@facets-cloud/facetsctl
Version:
245 lines (244 loc) • 11.2 kB
JavaScript
;
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 = [];