@axinom/mosaic-cli 0.20.0-rc.21 → 0.20.0-rc.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/commands/hosting/index.js +2 -0
  2. package/dist/commands/hosting/index.js.map +1 -1
  3. package/dist/commands/hosting/service/deploy/service-deploy-command.d.ts +3 -0
  4. package/dist/commands/hosting/service/deploy/service-deploy-command.js +72 -0
  5. package/dist/commands/hosting/service/deploy/service-deploy-command.js.map +1 -0
  6. package/dist/commands/hosting/service/deploy/service-deploy-options.d.ts +14 -0
  7. package/dist/commands/hosting/service/deploy/service-deploy-options.js +3 -0
  8. package/dist/commands/hosting/service/deploy/service-deploy-options.js.map +1 -0
  9. package/dist/commands/hosting/service/deploy/service-deploy.d.ts +9 -0
  10. package/dist/commands/hosting/service/deploy/service-deploy.js +239 -0
  11. package/dist/commands/hosting/service/deploy/service-deploy.js.map +1 -0
  12. package/dist/commands/hosting/service/service-commands.d.ts +2 -0
  13. package/dist/commands/hosting/service/service-commands.js +16 -0
  14. package/dist/commands/hosting/service/service-commands.js.map +1 -0
  15. package/dist/commands/hosting/service/undeploy/service-undeploy-command.d.ts +3 -0
  16. package/dist/commands/hosting/service/undeploy/service-undeploy-command.js +53 -0
  17. package/dist/commands/hosting/service/undeploy/service-undeploy-command.js.map +1 -0
  18. package/dist/commands/hosting/service/undeploy/service-undeploy-options.d.ts +11 -0
  19. package/dist/commands/hosting/service/undeploy/service-undeploy-options.js +3 -0
  20. package/dist/commands/hosting/service/undeploy/service-undeploy-options.js.map +1 -0
  21. package/dist/commands/hosting/service/undeploy/service-undeploy.d.ts +9 -0
  22. package/dist/commands/hosting/service/undeploy/service-undeploy.js +140 -0
  23. package/dist/commands/hosting/service/undeploy/service-undeploy.js.map +1 -0
  24. package/package.json +4 -4
  25. package/src/commands/hosting/index.ts +2 -0
  26. package/src/commands/hosting/service/deploy/service-deploy-command.ts +71 -0
  27. package/src/commands/hosting/service/deploy/service-deploy-options.ts +14 -0
  28. package/src/commands/hosting/service/deploy/service-deploy.ts +333 -0
  29. package/src/commands/hosting/service/service-commands.ts +23 -0
  30. package/src/commands/hosting/service/undeploy/service-undeploy-command.ts +53 -0
  31. package/src/commands/hosting/service/undeploy/service-undeploy-options.ts +11 -0
  32. package/src/commands/hosting/service/undeploy/service-undeploy.ts +188 -0
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateUndeploymentArgs = exports.undeployService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const mosaic_id_link_be_1 = require("@axinom/mosaic-id-link-be");
6
+ const mosaic_service_common_1 = require("@axinom/mosaic-service-common");
7
+ const axios_1 = require("axios");
8
+ const chalk_1 = require("chalk");
9
+ const getAxiosInstance = (hostingServiceBaseUrl, serviceAccountToken) => {
10
+ return axios_1.default.create({
11
+ baseURL: new URL(hostingServiceBaseUrl).toString(),
12
+ headers: {
13
+ Authorization: `Bearer ${serviceAccountToken}`,
14
+ 'content-type': 'application/json',
15
+ },
16
+ });
17
+ };
18
+ const undeployService = (args) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
19
+ var _a;
20
+ const serviceAccountToken = yield (0, mosaic_id_link_be_1.getServiceAccountToken)(args.idServiceAuthBaseURL, args.mosaicHostingClientId, args.mosaicHostingClientSecret);
21
+ const axiosInstance = getAxiosInstance(args.hostingServiceBaseURL, serviceAccountToken.accessToken);
22
+ let serviceDeploymentId;
23
+ if (!(0, mosaic_service_common_1.isNullOrWhitespace)(args.name)) {
24
+ serviceDeploymentId = yield getServiceDeploymentIdByName(args.name, axiosInstance);
25
+ }
26
+ else {
27
+ serviceDeploymentId = args.serviceDeploymentId;
28
+ }
29
+ if ((0, mosaic_service_common_1.isNullOrWhitespace)(serviceDeploymentId)) {
30
+ console.log((0, chalk_1.red)(`No Service Deployment was found for name [${args.name}].`));
31
+ return;
32
+ }
33
+ const serviceUneployment = JSON.stringify({
34
+ query: `mutation InitiateServiceUndeployment($serviceDeploymentId: UUID!) {
35
+ initiateServiceUndeployment(input: {serviceDeploymentId: $serviceDeploymentId}) {
36
+ status
37
+ serviceDeploymentId
38
+ }
39
+ }`,
40
+ variables: {
41
+ serviceDeploymentId,
42
+ },
43
+ });
44
+ try {
45
+ const response = yield axiosInstance.post('graphql', serviceUneployment);
46
+ if (response.data.errors !== undefined && response.data.errors.length > 0) {
47
+ console.log((0, chalk_1.red)(`Error while initiating undeployment for Service Deployment ID [${serviceDeploymentId}].`));
48
+ console.log(response.data.errors);
49
+ console.log();
50
+ }
51
+ else {
52
+ console.log((0, chalk_1.green)(`Undeployment for Service Deployment ID [${serviceDeploymentId}] initiated successfully.`));
53
+ console.log((0, chalk_1.yellow)(JSON.stringify((_a = response.data.data) === null || _a === void 0 ? void 0 : _a.initiateServiceUndeployment)));
54
+ console.log();
55
+ }
56
+ }
57
+ catch (error) {
58
+ console.log((0, chalk_1.red)(`Error while initiating undeployment for Service Deployment ID [${serviceDeploymentId}].`));
59
+ console.log((0, chalk_1.red)(JSON.stringify(error.message)));
60
+ }
61
+ });
62
+ exports.undeployService = undeployService;
63
+ /**
64
+ * Validate arguments for Service Undeployment
65
+ *
66
+ * @param args
67
+ * @returns
68
+ */
69
+ const validateUndeploymentArgs = (args) => {
70
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
71
+ const errorMessages = [];
72
+ const idServiceAuthBaseURL = (_b = (_a = args.idServiceAuthBaseURL) !== null && _a !== void 0 ? _a : process.env.ID_SERVICE_AUTH_BASE_URL) !== null && _b !== void 0 ? _b : '';
73
+ const hostingServiceBaseURL = (_d = (_c = args.hostingServiceBaseURL) !== null && _c !== void 0 ? _c : process.env.HOSTING_SERVICE_BASE_URL) !== null && _d !== void 0 ? _d : '';
74
+ const mosaicHostingClientId = (_f = (_e = args.mosaicHostingClientId) !== null && _e !== void 0 ? _e : process.env.MOSAIC_HOSTING_CLIENT_ID) !== null && _f !== void 0 ? _f : '';
75
+ const mosaicHostingClientSecret = (_h = (_g = args.mosaicHostingClientSecret) !== null && _g !== void 0 ? _g : process.env.MOSAIC_HOSTING_CLIENT_SECRET) !== null && _h !== void 0 ? _h : '';
76
+ const name = (_j = args.name) !== null && _j !== void 0 ? _j : '';
77
+ const serviceDeploymentId = (_k = args.serviceDeploymentId) !== null && _k !== void 0 ? _k : '';
78
+ if ((0, mosaic_service_common_1.isNullOrWhitespace)(idServiceAuthBaseURL)) {
79
+ errorMessages.push('[idServiceAuthBaseURL] is required.');
80
+ }
81
+ if ((0, mosaic_service_common_1.isNullOrWhitespace)(mosaicHostingClientId)) {
82
+ errorMessages.push('[clientId] is required.');
83
+ }
84
+ if ((0, mosaic_service_common_1.isNullOrWhitespace)(mosaicHostingClientSecret)) {
85
+ errorMessages.push('[clientSecret] is required.');
86
+ }
87
+ if ((0, mosaic_service_common_1.isNullOrWhitespace)(hostingServiceBaseURL)) {
88
+ errorMessages.push('[hostingServiceBaseURL] is required.');
89
+ }
90
+ if ((0, mosaic_service_common_1.isNullOrWhitespace)(name) && (0, mosaic_service_common_1.isNullOrWhitespace)(serviceDeploymentId)) {
91
+ errorMessages.push('Either the [name] or [serviceDeploymentId] must be provided to initiate the service undeployment.');
92
+ }
93
+ if (!(0, mosaic_service_common_1.isNullOrWhitespace)(name) && !(0, mosaic_service_common_1.isNullOrWhitespace)(serviceDeploymentId)) {
94
+ errorMessages.push('Only one of [name] or [serviceDeploymentId] arguments must be provided. Both cannot be present.');
95
+ }
96
+ return [
97
+ {
98
+ name,
99
+ serviceDeploymentId,
100
+ idServiceAuthBaseURL,
101
+ mosaicHostingClientId,
102
+ mosaicHostingClientSecret,
103
+ hostingServiceBaseURL,
104
+ },
105
+ errorMessages,
106
+ ];
107
+ };
108
+ exports.validateUndeploymentArgs = validateUndeploymentArgs;
109
+ const getServiceDeploymentIdByName = (name, axiosInstance) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
110
+ var _b, _c, _d;
111
+ const deploymentInfo = JSON.stringify({
112
+ query: `query GetServiceDeploymentIdByName($name: String!) {
113
+ serviceDeployments(condition: {name: $name}) {
114
+ nodes {
115
+ id
116
+ }
117
+ }
118
+ }`,
119
+ variables: {
120
+ name,
121
+ },
122
+ });
123
+ try {
124
+ const response = yield axiosInstance.post('graphql', deploymentInfo);
125
+ if (response.data.errors !== undefined && response.data.errors.length > 0) {
126
+ console.log((0, chalk_1.red)(`Error while retrieving deployment id for [${name}].`));
127
+ console.log(response.data.errors);
128
+ console.log();
129
+ }
130
+ else {
131
+ return (_d = (_c = (_b = response.data.data) === null || _b === void 0 ? void 0 : _b.serviceDeployments) === null || _c === void 0 ? void 0 : _c.nodes[0]) === null || _d === void 0 ? void 0 : _d.id;
132
+ }
133
+ }
134
+ catch (error) {
135
+ console.log((0, chalk_1.red)(`Error while retrieving deployment id for [${name}].`));
136
+ console.log(JSON.stringify(error.message));
137
+ console.log();
138
+ }
139
+ });
140
+ //# sourceMappingURL=service-undeploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-undeploy.js","sourceRoot":"","sources":["../../../../../src/commands/hosting/service/undeploy/service-undeploy.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-cli",
3
- "version": "0.20.0-rc.21",
3
+ "version": "0.20.0-rc.23",
4
4
  "description": "The Axinom Mosaic CLI",
5
5
  "author": "Axinom",
6
6
  "license": "PROPRIETARY",
@@ -33,8 +33,8 @@
33
33
  "@asyncapi/diff": "^0.4.1",
34
34
  "@asyncapi/modelina": "^1.8.4",
35
35
  "@asyncapi/parser": "^2.0.2",
36
- "@axinom/mosaic-id-link-be": "^0.13.4-rc.21",
37
- "@axinom/mosaic-service-common": "^0.35.0-rc.21",
36
+ "@axinom/mosaic-id-link-be": "^0.13.4-rc.23",
37
+ "@axinom/mosaic-service-common": "^0.35.0-rc.23",
38
38
  "@graphql-inspector/core": "^3.1.2",
39
39
  "@stoplight/spectral": "^5.9.1",
40
40
  "axios": "^0.19.2",
@@ -68,5 +68,5 @@
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
71
- "gitHead": "df13367a88d317969e5b2c3960268938fa07c41f"
71
+ "gitHead": "bf1cd2c57164e8463fd832b6d8c8fb8376281a25"
72
72
  }
@@ -1,6 +1,7 @@
1
1
  import { CommandModule } from 'yargs';
2
2
  import { manifestCommands } from './manifest/manifest-commands';
3
3
  import { piletCommands } from './pilet/pilet-commands';
4
+ import { serviceCommands } from './service/service-commands';
4
5
 
5
6
  export const hosting: CommandModule<unknown, unknown> = {
6
7
  command: 'hosting',
@@ -13,6 +14,7 @@ export const hosting: CommandModule<unknown, unknown> = {
13
14
  'Deployment Manifest related actions',
14
15
  manifestCommands,
15
16
  )
17
+ .command('service', 'Service Deployment related actions', serviceCommands)
16
18
  .demandCommand(),
17
19
  handler: (_args) => {
18
20
  console.log('Please pick a sub command related to hosting.');
@@ -0,0 +1,71 @@
1
+ import { red, yellow } from 'chalk';
2
+ import { CommandModule } from 'yargs';
3
+ import { deployService, validateDeploymentArgs } from './service-deploy';
4
+ import { GetServiceDeployOptions } from './service-deploy-options';
5
+
6
+ export const serviceDeploy: CommandModule<unknown, GetServiceDeployOptions> = {
7
+ builder: (yargs) => {
8
+ return yargs
9
+ .option('name', {
10
+ describe: 'Name of the Deployment',
11
+ alias: 'n',
12
+ string: true,
13
+ demandOption: true,
14
+ })
15
+ .option('serviceId', {
16
+ describe:
17
+ 'Service ID the deployment must be done for. (If a deployment manifest is found in the current directory, the serviceId will be derived from it.)',
18
+ alias: 'i',
19
+ string: true,
20
+ })
21
+ .option('containerImageVersion', {
22
+ describe: 'Version of the container image to be used in the deployment',
23
+ alias: 'v',
24
+ string: true,
25
+ demandOption: true,
26
+ })
27
+ .option('manifestName', {
28
+ describe: 'Name of the deployment manifest',
29
+ alias: 'm',
30
+ string: true,
31
+ demandOption: true,
32
+ })
33
+ .option('pilets', {
34
+ describe:
35
+ 'Pilet(s) to be used in the deployment. The pilet name should match the format package-name@version => media-workflows@1.5.7',
36
+ alias: 'p',
37
+ array: true,
38
+ string: true,
39
+ })
40
+ .option('mosaicHostingClientId', {
41
+ describe: 'Service Account Client ID to authenticate',
42
+ alias: 'c',
43
+ string: true,
44
+ })
45
+ .option('mosaicHostingClientSecret', {
46
+ describe: 'Service Account Client Secret to authenticate',
47
+ alias: 's',
48
+ string: true,
49
+ })
50
+ .option('idServiceAuthBaseURL', {
51
+ describe: 'ID Service Authentication Endpoint Base URL',
52
+ alias: 'a',
53
+ string: true,
54
+ })
55
+ .option('hostingServiceBaseURL', {
56
+ describe: 'Hosting Service Base URL',
57
+ alias: 'h',
58
+ string: true,
59
+ });
60
+ },
61
+ handler: async (args) => {
62
+ const [validatedArgs, errorMessages] = validateDeploymentArgs(args);
63
+ if (errorMessages.length > 0) {
64
+ console.log(red('Some required arguments are missing.'));
65
+ console.log(yellow(errorMessages));
66
+ console.log();
67
+ } else {
68
+ await deployService(validatedArgs);
69
+ }
70
+ },
71
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Options for Deploying a Service
3
+ */
4
+ export interface GetServiceDeployOptions {
5
+ name: string;
6
+ serviceId?: string;
7
+ containerImageVersion: string;
8
+ manifestName: string;
9
+ pilets?: string[];
10
+ mosaicHostingClientId?: string;
11
+ mosaicHostingClientSecret?: string;
12
+ idServiceAuthBaseURL?: string;
13
+ hostingServiceBaseURL?: string;
14
+ }
@@ -0,0 +1,333 @@
1
+ import { getServiceAccountToken } from '@axinom/mosaic-id-link-be';
2
+ import { isNullOrWhitespace } from '@axinom/mosaic-service-common';
3
+ import axios, { AxiosError, AxiosInstance } from 'axios';
4
+ import { green, red, yellow } from 'chalk';
5
+ import { readFileSync } from 'fs';
6
+ import * as yaml from 'js-yaml';
7
+ import { GetServiceDeployOptions } from './service-deploy-options';
8
+
9
+ const getAxiosInstance = (
10
+ hostingServiceBaseUrl: string,
11
+ serviceAccountToken: string,
12
+ ): AxiosInstance => {
13
+ return axios.create({
14
+ baseURL: new URL(hostingServiceBaseUrl).toString(),
15
+ headers: {
16
+ Authorization: `Bearer ${serviceAccountToken}`,
17
+ 'content-type': 'application/json',
18
+ },
19
+ });
20
+ };
21
+
22
+ export const deployService = async (
23
+ args: Required<GetServiceDeployOptions>,
24
+ ): Promise<void> => {
25
+ const serviceAccountToken = await getServiceAccountToken(
26
+ args.idServiceAuthBaseURL,
27
+ args.mosaicHostingClientId,
28
+ args.mosaicHostingClientSecret,
29
+ );
30
+
31
+ const axiosInstance: AxiosInstance = getAxiosInstance(
32
+ args.hostingServiceBaseURL,
33
+ serviceAccountToken.accessToken,
34
+ );
35
+
36
+ const deploymentDetails = await retrieveServiceDeploymentDetails(
37
+ args,
38
+ axiosInstance,
39
+ );
40
+
41
+ if (deploymentDetails === undefined) {
42
+ return;
43
+ }
44
+
45
+ const serviceDeployment = JSON.stringify({
46
+ query: `mutation initiateServiceDeployment($serviceDefinitionId: UUID!, $containerImageVersion: String!, $serviceDeploymentConfigurationId: UUID!, $servicePiletArtifactIds: [UUID!]!, $name: String) {
47
+ initiateServiceDeployment(
48
+ input: {serviceDefinitionId: $serviceDefinitionId, containerImageVersion: $containerImageVersion, serviceDeploymentConfigurationId: $serviceDeploymentConfigurationId, servicePiletArtifactIds: $servicePiletArtifactIds, name: $name}
49
+ ) {
50
+ serviceDeploymentId
51
+ name
52
+ status
53
+ }
54
+ }`,
55
+ variables: {
56
+ serviceDefinitionId: deploymentDetails.serviceDefinitionId,
57
+ containerImageVersion: args.containerImageVersion,
58
+ serviceDeploymentConfigurationId: deploymentDetails.deploymentManifestId,
59
+ servicePiletArtifactIds: deploymentDetails.piletArtifactIds,
60
+ name: args.name,
61
+ },
62
+ });
63
+
64
+ try {
65
+ const response = await axiosInstance.post('graphql', serviceDeployment);
66
+ if (response.data.errors !== undefined && response.data.errors.length > 0) {
67
+ console.log(red(`Deployment for Service ID [${args.serviceId}] failed.`));
68
+ if (
69
+ response.data.errors[0].message ===
70
+ 'Attempt to create or update an element failed, as it would have resulted in a duplicate element.'
71
+ ) {
72
+ console.log(
73
+ yellow(
74
+ `Hint: The deployment likely failed because the deployment name [${args.name}] is already in use. Please try with a different name.`,
75
+ ),
76
+ );
77
+ } else {
78
+ console.log(red(response.data.errors[0].message));
79
+ }
80
+ console.log();
81
+ } else {
82
+ console.log(
83
+ green(
84
+ `Deployment for Service ID [${args.serviceId}] initiated successfully. It may take some time to complete the deployment.`,
85
+ ),
86
+ );
87
+ console.log(
88
+ yellow(JSON.stringify(response.data.data?.initiateServiceDeployment)),
89
+ );
90
+ console.log();
91
+ }
92
+ } catch (error) {
93
+ console.log(
94
+ red(
95
+ `Error while performing deployment for Service ID [${args.serviceId}].`,
96
+ ),
97
+ );
98
+ console.log(red(JSON.stringify((error as AxiosError).message)));
99
+ }
100
+ };
101
+
102
+ /**
103
+ * Validate arguments for Service Deployment
104
+ *
105
+ * @param args
106
+ * @returns
107
+ */
108
+ export const validateDeploymentArgs = (
109
+ args: GetServiceDeployOptions,
110
+ ): [Required<GetServiceDeployOptions>, string[]] => {
111
+ const errorMessages: string[] = [];
112
+
113
+ const idServiceAuthBaseURL =
114
+ args.idServiceAuthBaseURL ?? process.env.ID_SERVICE_AUTH_BASE_URL ?? '';
115
+ const mosaicHostingClientId =
116
+ args.mosaicHostingClientId ?? process.env.MOSAIC_HOSTING_CLIENT_ID ?? '';
117
+ const mosaicHostingClientSecret =
118
+ args.mosaicHostingClientSecret ??
119
+ process.env.MOSAIC_HOSTING_CLIENT_SECRET ??
120
+ '';
121
+ const serviceId = args.serviceId ?? getServiceIdFromManifest() ?? '';
122
+ const hostingServiceBaseURL =
123
+ args.hostingServiceBaseURL ?? process.env.HOSTING_SERVICE_BASE_URL ?? '';
124
+
125
+ const manifestName = args.manifestName;
126
+ const containerImageVersion = args.containerImageVersion;
127
+ let pilets = args.pilets;
128
+
129
+ if (isNullOrWhitespace(idServiceAuthBaseURL)) {
130
+ errorMessages.push('[idServiceAuthBaseURL] is required.');
131
+ }
132
+ if (isNullOrWhitespace(mosaicHostingClientId)) {
133
+ errorMessages.push('[clientId] is required.');
134
+ }
135
+ if (isNullOrWhitespace(mosaicHostingClientSecret)) {
136
+ errorMessages.push('[clientSecret] is required.');
137
+ }
138
+ if (isNullOrWhitespace(serviceId)) {
139
+ errorMessages.push(
140
+ '[serviceId] is required. If no serviceId is given through arguments, mosaic-hosting-deployment-manifest.yaml file in the current directory is checked to derive the serviceId.',
141
+ );
142
+ }
143
+ if (isNullOrWhitespace(hostingServiceBaseURL)) {
144
+ errorMessages.push('[hostingServiceBaseURL] is required.');
145
+ }
146
+ if (isNullOrWhitespace(manifestName)) {
147
+ errorMessages.push(
148
+ '[manifestName] is required and cannot be an empty string.',
149
+ );
150
+ }
151
+ if (isNullOrWhitespace(containerImageVersion)) {
152
+ errorMessages.push(
153
+ '[containerImageVersion] is required and cannot be an empty string.',
154
+ );
155
+ }
156
+
157
+ if (isNullOrWhitespace(pilets)) {
158
+ pilets = [];
159
+ }
160
+
161
+ return [
162
+ {
163
+ name: args.name,
164
+ idServiceAuthBaseURL,
165
+ mosaicHostingClientId,
166
+ mosaicHostingClientSecret,
167
+ serviceId,
168
+ manifestName,
169
+ pilets,
170
+ containerImageVersion: containerImageVersion,
171
+ hostingServiceBaseURL,
172
+ },
173
+ errorMessages,
174
+ ];
175
+ };
176
+
177
+ const getServiceIdFromManifest = (): string | undefined => {
178
+ try {
179
+ const doc = yaml.load(
180
+ readFileSync('./mosaic-hosting-deployment-manifest.yaml', 'utf8'),
181
+ );
182
+ if (!isNullOrWhitespace(doc)) {
183
+ return (doc as any).serviceId;
184
+ }
185
+ } catch (error) {
186
+ return undefined;
187
+ }
188
+ };
189
+
190
+ const retrieveServiceDeploymentDetails = async (
191
+ args: Required<GetServiceDeployOptions>,
192
+ axiosInstance: AxiosInstance,
193
+ ): Promise<
194
+ | {
195
+ serviceDefinitionId: string;
196
+ deploymentManifestId: string;
197
+ piletArtifactIds: string[];
198
+ }
199
+ | undefined
200
+ > => {
201
+ const piletNamingErrors: string[] = [];
202
+
203
+ const piletFilter = args.pilets.map((pilet) => {
204
+ const nameAndVersion = pilet.split('@');
205
+ if (nameAndVersion.length !== 2) {
206
+ piletNamingErrors.push(`Pilet name [${pilet}] is invalid.`);
207
+ return;
208
+ }
209
+ return {
210
+ name: { equalTo: nameAndVersion[0] },
211
+ packageVersion: { equalTo: nameAndVersion[1] },
212
+ serviceId: { equalTo: args.serviceId },
213
+ };
214
+ });
215
+
216
+ if (piletNamingErrors.length > 0) {
217
+ console.log(red(piletNamingErrors));
218
+ console.log(
219
+ yellow(
220
+ `Pilet names must match the pattern [pilet-name@package-version]. i.e.: media-workflows@2.0.1.`,
221
+ ),
222
+ );
223
+ console.log();
224
+ return;
225
+ }
226
+
227
+ const deploymentInfo = JSON.stringify({
228
+ query: `query GetDeploymentInfo($serviceId: String!, $manifestName: String!, $pilets: ServicePiletArtifactFilter ) {
229
+ serviceDefinitions(condition: {serviceId: $serviceId}) {
230
+ nodes {
231
+ id
232
+ }
233
+ }
234
+ serviceDeploymentManifests(
235
+ filter: {name: {equalTo: $manifestName}, serviceId: {equalTo: $serviceId}}
236
+ ) {
237
+ nodes {
238
+ id
239
+ }
240
+ }
241
+ servicePiletArtifacts(
242
+ filter: $pilets
243
+ ) {
244
+ nodes {
245
+ id
246
+ }
247
+ }
248
+ }`,
249
+ variables: {
250
+ serviceId: args.serviceId,
251
+ manifestName: args.manifestName,
252
+ pilets: {
253
+ or: piletFilter,
254
+ },
255
+ },
256
+ });
257
+
258
+ let serviceDefinitionId: string | undefined;
259
+ let deploymentManifestId: string | undefined;
260
+ let piletArtifactIds: string[] = [];
261
+
262
+ try {
263
+ const response = await axiosInstance.post('graphql', deploymentInfo);
264
+ if (response.data.errors !== undefined && response.data.errors.length > 0) {
265
+ console.log(
266
+ red(`Error while retrieving deployment info for [${args.serviceId}].`),
267
+ );
268
+ console.log(response.data.errors);
269
+ console.log();
270
+ } else {
271
+ serviceDefinitionId =
272
+ response.data.data?.serviceDefinitions?.nodes[0]?.id;
273
+ deploymentManifestId =
274
+ response.data.data?.serviceDeploymentManifests?.nodes[0]?.id;
275
+ piletArtifactIds = response.data.data?.servicePiletArtifacts?.nodes?.map(
276
+ (pilet) => pilet.id,
277
+ );
278
+ console.log(
279
+ green(
280
+ `Deployment info retrieved successfully for [${args.serviceId}].`,
281
+ ),
282
+ );
283
+ console.log(green(`Service Definition ID:\t${serviceDefinitionId}`));
284
+ console.log(green(`Deployment Manifest ID:\t${deploymentManifestId}`));
285
+ console.log(
286
+ green(`Pilet Artifacts ID(s):\t${piletArtifactIds.join(',')}`),
287
+ );
288
+ console.log();
289
+ }
290
+ } catch (error) {
291
+ console.log(
292
+ red(`Error while retrieving deployment info for ${args.serviceId}.`),
293
+ );
294
+ console.log(JSON.stringify((error as AxiosError).message));
295
+ console.log();
296
+ }
297
+
298
+ if (isNullOrWhitespace(serviceDefinitionId)) {
299
+ console.log(
300
+ red(
301
+ `No Service Definition found for Service ID [${args.serviceId}]. Cannot proceed with deployment.`,
302
+ ),
303
+ );
304
+ console.log();
305
+ return;
306
+ }
307
+
308
+ if (isNullOrWhitespace(deploymentManifestId)) {
309
+ console.log(
310
+ red(
311
+ `No Deployment Manifest found for name [${args.manifestName}] and Service ID [${args.serviceId}]. Cannot proceed with deployment.`,
312
+ ),
313
+ );
314
+ console.log();
315
+ return;
316
+ }
317
+
318
+ if (args.pilets.length !== piletArtifactIds.length) {
319
+ console.log(
320
+ red(
321
+ `Pilet artifacts were not found for the given pilet names. Cannot proceed with deployment.`,
322
+ ),
323
+ );
324
+ console.log();
325
+ return;
326
+ }
327
+
328
+ return {
329
+ serviceDefinitionId,
330
+ deploymentManifestId,
331
+ piletArtifactIds,
332
+ };
333
+ };
@@ -0,0 +1,23 @@
1
+ import { CommandModule } from 'yargs';
2
+ import { serviceDeploy } from './deploy/service-deploy-command';
3
+ import { serviceUndeploy } from './undeploy/service-undeploy-command';
4
+
5
+ export const serviceCommands: CommandModule<unknown, unknown> = {
6
+ builder: (yargs) =>
7
+ yargs
8
+ .command(
9
+ 'deploy',
10
+ 'Deploy a service through Hosting Service.',
11
+ serviceDeploy,
12
+ )
13
+ .demandCommand()
14
+ .command(
15
+ 'undeploy',
16
+ 'Undeploy a service through Hosting Service.',
17
+ serviceUndeploy,
18
+ )
19
+ .demandCommand(),
20
+ handler: () => {
21
+ console.log('Please pick an action related to hosting service command.');
22
+ },
23
+ };
@@ -0,0 +1,53 @@
1
+ import { red, yellow } from 'chalk';
2
+ import { CommandModule } from 'yargs';
3
+ import { undeployService, validateUndeploymentArgs } from './service-undeploy';
4
+ import { GetServiceUndeployOptions } from './service-undeploy-options';
5
+
6
+ export const serviceUndeploy: CommandModule<
7
+ unknown,
8
+ GetServiceUndeployOptions
9
+ > = {
10
+ builder: (yargs) => {
11
+ return yargs
12
+ .option('name', {
13
+ describe: 'Name of the Deployment',
14
+ alias: 'n',
15
+ string: true,
16
+ })
17
+ .option('serviceDeploymentId', {
18
+ describe: 'ID of the Deployment',
19
+ alias: 'i',
20
+ string: true,
21
+ })
22
+ .option('mosaicHostingClientId', {
23
+ describe: 'Service Account Client ID to authenticate',
24
+ alias: 'c',
25
+ string: true,
26
+ })
27
+ .option('mosaicHostingClientSecret', {
28
+ describe: 'Service Account Client Secret to authenticate',
29
+ alias: 's',
30
+ string: true,
31
+ })
32
+ .option('idServiceAuthBaseURL', {
33
+ describe: 'ID Service Authentication Endpoint Base URL',
34
+ alias: 'a',
35
+ string: true,
36
+ })
37
+ .option('hostingServiceBaseURL', {
38
+ describe: 'Hosting Service Base URL',
39
+ alias: 'h',
40
+ string: true,
41
+ });
42
+ },
43
+ handler: async (args) => {
44
+ const [validatedArgs, errorMessages] = validateUndeploymentArgs(args);
45
+ if (errorMessages.length > 0) {
46
+ console.log(red('Some required arguments are missing.'));
47
+ console.log(yellow(errorMessages));
48
+ console.log();
49
+ } else {
50
+ await undeployService(validatedArgs);
51
+ }
52
+ },
53
+ };