@adpt/cloud
Version:
AdaptJS cloud component library
177 lines • 6.66 kB
JavaScript
"use strict";
/*
* Copyright 2019 Unbounded Systems, LLC
*
* 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 });
const tslib_1 = require("tslib");
const core_1 = require("@adpt/core");
const utils_1 = require("@adpt/utils");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const json_stable_stringify_1 = tslib_1.__importDefault(require("json-stable-stringify"));
const path = tslib_1.__importStar(require("path"));
const action_1 = require("../action");
const cli_1 = require("./cli");
/**
* Locally builds a docker image
*
* @remarks
* See {@link docker.LocalDockerImageProps}.
*
* @public
*/
class LocalDockerImage extends action_1.Action {
constructor(props) {
super(props);
// Even though we have a custom deployedWhen, don't normally show
// status from this component, unless there's an active action.
this.deployedWhenIsTrivial = true;
/** @internal */
this.deployedWhen = (goalStatus) => {
if (goalStatus === core_1.DeployStatus.Destroyed)
return true;
if (this.buildComplete())
return true;
if (this.state.imagePropsJson &&
this.state.imagePropsJson !== this.imagePropsJson) {
return core_1.waiting("Waiting for Docker image to be re-built");
}
return core_1.waiting("Waiting for Docker image to be built");
};
this.options_ = Object.assign({}, LocalDockerImage.defaultProps.options, (props.options || {}));
if (!props.dockerfile && !props.dockerfileName) {
throw new Error(`LocalDockerImage: one of dockerfile or ` +
`dockerfileName must be given`);
}
}
/*
* Public instance properties/methods
*/
buildComplete() { return this.image() != null; }
ready() { return this.buildComplete(); }
image() {
if (this.image_ == null) {
if (this.state.image != null &&
// Ensure we've rebuilt at least once this OpID
this.state.deployOpID === this.deployInfo.deployOpID &&
// And ensure the current build matches current props
this.state.imagePropsJson === this.imagePropsJson) {
this.image_ = this.state.image;
}
}
return this.image_;
}
async pushTo(registryUrl, newTag) {
const im = this.latestImage();
if (!im)
return undefined;
newTag = newTag || im.nameTag;
if (!newTag) {
throw new Error(`Unable to push image to registry: no nameTag ` +
`set for this image and new tag not provided`);
}
const fullTag = `${registryUrl}/${newTag}`;
const globals = cli_1.pickGlobals(this.options_);
await cli_1.dockerTag(Object.assign({ existing: im.id, newTag: fullTag }, globals));
await cli_1.dockerPush(Object.assign({ nameTag: fullTag }, globals));
return {
id: im.id,
nameTag: fullTag,
};
}
latestImage() {
return this.image_ || this.state.image;
}
/**
* User-facing name to display in status messages.
*/
displayName() { return this.options_.imageName; }
/**
* Implementations for Action base class
* @internal
*/
async shouldAct(op) {
let imgName = this.options_.imageName || "";
if (imgName)
imgName = ` '${imgName}'`;
if (op === core_1.ChangeType.delete)
return false;
if (this.buildComplete())
return false;
return {
act: true,
detail: `Building Docker image${imgName}`,
};
}
/**
* Implementations for Action base class
* @internal
*/
async action(op, ctx) {
const options = Object.assign({}, this.options_, { deployID: ctx.buildData.deployID });
const prevUniqueTag = this.state.prevUniqueTag;
if (op === core_1.ChangeType.delete) {
throw new utils_1.InternalError(`Delete action should not happen due to check in shouldAct`);
}
let dockerfile = this.props.dockerfile;
if (!dockerfile) {
if (!this.props.dockerfileName) {
throw new utils_1.InternalError(`dockerfileName should not be null`);
}
dockerfile = (await fs_extra_1.default.readFile(this.props.dockerfileName)).toString();
}
const stages = this.props.stages || [];
const image = await cli_1.withFilesImage(this.props.files, options, async (img) => {
if (img)
stages.push({ image: img.id, name: "files" });
const stageConfig = stages
.map((s) => `FROM ${s.image} as ${s.name}`)
.join("\n");
if (stageConfig) {
dockerfile = `${stageConfig}\n\n${dockerfile}`;
}
let contextDir = this.props.contextDir || ".";
contextDir = path.resolve(contextDir);
return cli_1.dockerBuild("-", contextDir, Object.assign({}, options, { prevUniqueTag, stdin: dockerfile }));
});
this.image_ = image;
this.setState({
deployOpID: this.deployInfo.deployOpID,
image,
imagePropsJson: this.imagePropsJson,
prevUniqueTag: options.uniqueTag ? image.nameTag : undefined,
});
}
/*
* Component methods
*/
/** @internal */
initialState() { return {}; }
/** @internal */
get imagePropsJson() {
if (!this.imagePropsJson_) {
const _a = this.props, { handle: _h, key } = _a, imageProps = tslib_1.__rest(_a, ["handle", "key"]);
this.imagePropsJson_ = json_stable_stringify_1.default(imageProps);
}
return this.imagePropsJson_;
}
}
LocalDockerImage.defaultProps = {
options: {
dockerHost: process.env.DOCKER_HOST,
forceRm: true,
},
};
exports.LocalDockerImage = LocalDockerImage;
//# sourceMappingURL=LocalDockerImage.js.map