UNPKG

8.2 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const sdk_1 = require("@cto.ai/sdk");
5const debug_1 = tslib_1.__importDefault(require("debug"));
6const JSONStream_1 = tslib_1.__importDefault(require("JSONStream"));
7const through2_1 = tslib_1.__importDefault(require("through2"));
8const path = tslib_1.__importStar(require("path"));
9const fs = tslib_1.__importStar(require("fs-extra"));
10const CustomErrors_1 = require("../errors/CustomErrors");
11const Error_1 = require("./Error");
12const get_docker_1 = tslib_1.__importDefault(require("../utils/get-docker"));
13const debug = debug_1.default('ops:ImageService');
14class ImageService {
15 constructor(error = new Error_1.ErrorService()) {
16 this.error = error;
17 this.log = console.log;
18 this.checkLocalImage = async (opImageUrl) => {
19 const docker = await get_docker_1.default(console, 'ImageService');
20 const list = await docker.listImages();
21 return list
22 .map(this.imageFilterPredicate(opImageUrl))
23 .find((repoTag) => !!repoTag);
24 };
25 this.imageFilterPredicate = (repo) => ({ RepoTags }) => {
26 if (!RepoTags) {
27 return;
28 }
29 return RepoTags.find((repoTag) => repoTag.includes(repo));
30 };
31 this.pull = async (op, authconfig) => {
32 this.log(`šŸ”‹ Pulling ${sdk_1.ux.colors.dim(op.name)} from registry...\n`);
33 const docker = await get_docker_1.default(console, 'ImageServicePull');
34 const stream = await docker.pull(op.image || '', { authconfig });
35 if (!stream) {
36 throw new Error('No stream');
37 }
38 const parser = await this.setParser(op, this.getProgressBarText);
39 await new Promise(this.updateStatusBar(stream, parser));
40 sdk_1.ux.spinner.stop(sdk_1.ux.colors.green('Done!'));
41 const msg = `${sdk_1.ux.colors.italic.bold(`${op.name}:${op.id}`)}`;
42 this.log(`\nšŸ™Œ Saved ${msg} locally! \n`);
43 };
44 this.setParser = (op, getFn) => {
45 const bar = sdk_1.ux.progress.init({
46 format: sdk_1.ux.colors.callOutCyan('{bar} {percentage}% | Status: {speed} '),
47 barCompleteChar: '\u2588',
48 barIncompleteChar: '\u2591',
49 });
50 bar.start(100, 0, { speed: 'šŸ Starting...' });
51 const layers = {};
52 const parser = through2_1.default.obj((chunk, _enc, callback) => {
53 const { id, status, progressDetail: p } = chunk;
54 const progress = p && p.current ? (p.current / p.total) * 100 : 0;
55 const { speed } = getFn(status, op);
56 if (id) {
57 layers[id] = id;
58 }
59 if (speed) {
60 bar.update(progress, { speed });
61 }
62 callback();
63 });
64 const _pipe = parser.pipe;
65 parser.pipe = dest => _pipe(dest);
66 return { parser, bar };
67 };
68 this.getProgressBarText = (status, { name }) => {
69 const mapping = {
70 [`Pulling from ${name}`]: `āœ… Pulling from ${name}...`,
71 'Already exists': 'āœ… Already exists!',
72 Waiting: 'ā± Waiting...',
73 Downloading: 'šŸ‘‡ Downloading...',
74 'Download complete': 'šŸ‘‡ Downloaded!',
75 Extracting: 'šŸ“¦ Unpacking...',
76 'Pulling fs layer': 'šŸ‘ Pulling layers...',
77 'Pull complete': 'šŸŽ‰ Pull Complete!',
78 };
79 return { speed: mapping[status] };
80 };
81 this.updateStatusBar = (stream, { parser, bar }) => async (resolve, reject) => {
82 const allData = [];
83 const size = 100;
84 stream
85 .pipe(JSONStream_1.default.parse())
86 .pipe(parser)
87 .on('data', data => allData.push(data))
88 .on('end', async (err) => {
89 for (let i = 0; i < size; i++) {
90 bar.update(100 - size / i);
91 await sdk_1.ux.wait(5);
92 }
93 bar.update(100);
94 bar.stop();
95 debug('%O', err);
96 return err ? reject(err) : resolve(allData);
97 });
98 };
99 this.checkIfDockerfileExists = (opPath) => {
100 const pathToDockerfile = path.join(path.resolve(opPath), 'Dockerfile');
101 return fs.existsSync(pathToDockerfile);
102 };
103 this.build = async (tag, opPath, op) => {
104 try {
105 const dockerfileExists = this.checkIfDockerfileExists(opPath);
106 if (!dockerfileExists) {
107 throw new Error(`Unable to build an image for this op. If you are inside your op directory, please run ${sdk_1.ux.colors.green('$')} ${sdk_1.ux.colors.italic.dim('ops run .')} or ${sdk_1.ux.colors.green('$')} ${sdk_1.ux.colors.italic.dim('ops build .')} instead.\n`);
108 }
109 const all = [];
110 const errors = [];
111 const log = this.log;
112 const parser = through2_1.default.obj(function (chunk, _enc, cb) {
113 if (chunk.stream && chunk.stream !== '\n') {
114 this.push(chunk.stream.replace('\n', ''));
115 log(chunk.stream.replace('\n', ''));
116 all.push(chunk);
117 }
118 else if (chunk.errorDetail) {
119 debug(chunk.errorDetail);
120 errors.push(chunk.errorDetail.message);
121 }
122 cb();
123 });
124 const _pipe = parser.pipe;
125 parser.pipe = function (dest) {
126 return _pipe(dest);
127 };
128 await new Promise(async function (resolve, reject) {
129 const docker = await get_docker_1.default(console, 'build');
130 if (docker) {
131 const stream = await docker
132 .buildImage({ context: opPath, src: op.src }, { t: tag })
133 .catch(err => {
134 debug('%O', err);
135 throw new CustomErrors_1.DockerBuildImageError(err);
136 });
137 if (stream) {
138 stream
139 .pipe(JSONStream_1.default.parse())
140 .pipe(parser)
141 .on('data', (d, data) => {
142 all.push(d);
143 })
144 .on('end', async function () {
145 if (errors.length) {
146 return reject(new CustomErrors_1.DockerBuildImageError(errors[0]));
147 }
148 log('\nāš”ļø Verifying...');
149 const bar = sdk_1.ux.progress.init();
150 bar.start(100, 0);
151 for (let i = 0; i < all.length; i++) {
152 bar.update(100 - all.length / i);
153 await sdk_1.ux.wait(50);
154 }
155 bar.update(100);
156 bar.stop();
157 log(`\nšŸ’» Run ${sdk_1.ux.colors.green('$')} ${sdk_1.ux.colors.italic.dim('ops run ' + op.name)} to test your op.`);
158 log(`šŸ“¦ Run ${sdk_1.ux.colors.green('$')} ${sdk_1.ux.colors.italic.dim('ops publish ' + opPath)} to share your op. \n`);
159 resolve();
160 });
161 }
162 }
163 });
164 }
165 catch (err) {
166 debug('%O', err);
167 this.error.handleError({ err });
168 }
169 };
170 }
171}
172exports.ImageService = ImageService;