@ibm-cloud/cd-tools 1.6.0 → 1.7.0

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.
package/README.md CHANGED
@@ -132,9 +132,9 @@ Copies a toolchain, including tool integrations and Tekton pipelines, to another
132
132
 
133
133
  Examples:
134
134
  export IBMCLOUD_API_KEY='...'
135
- npx @ibm-cloud/cd-migration-tools copy-toolchain -c ${TOOLCHAIN_CRN} -r us-south
135
+ npx @ibm-cloud/cd-tools copy-toolchain -c ${TOOLCHAIN_CRN} -r us-south
136
136
  Copy a toolchain to the Dallas region with the same name, in the same resource group.
137
- npx @ibm-cloud/cd-migration-tools copy-toolchain -c ${TOOLCHAIN_CRN} -r eu-de -n new-toolchain-name -g new-resource-group --apikey ${APIKEY}
137
+ npx @ibm-cloud/cd-tools copy-toolchain -c ${TOOLCHAIN_CRN} -r eu-de -n new-toolchain-name -g new-resource-group --apikey ${APIKEY}
138
138
  Copy a toolchain to the Frankfurt region with the specified name and target resource group, using the given API key
139
139
 
140
140
  Environment Variables:
@@ -322,10 +322,7 @@ async function main(options) {
322
322
  }
323
323
 
324
324
  // create toolchain, which invokes script to create s2s if applicable
325
- await runTerraformApply(true, outputDir, verbosity, `ibm_cd_toolchain.${toolchainTfName}`).catch((err) => {
326
- logger.error(err, LOG_STAGES.tf);
327
- applyErrors = true;
328
- });
325
+ await runTerraformApply(true, outputDir, verbosity, `ibm_cd_toolchain.${toolchainTfName}`);
329
326
 
330
327
  // create the rest
331
328
  await runTerraformApply(skipUserConfirmation, outputDir, verbosity).catch((err) => {
@@ -7,15 +7,19 @@
7
7
  * Contract with IBM Corp.
8
8
  */
9
9
 
10
- import { Command, Option } from 'commander';
10
+ import { Command } from 'commander';
11
11
  import axios from 'axios';
12
12
  import readline from 'readline/promises';
13
+ import { writeFile } from 'fs/promises';
13
14
  import { TARGET_REGIONS, SOURCE_REGIONS } from '../config.js';
14
15
 
16
+ const HTTP_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes default
17
+
15
18
  class GitLabClient {
16
19
  constructor(baseURL, token) {
17
20
  this.client = axios.create({
18
21
  baseURL: baseURL.endsWith('/') ? `${baseURL}api/v4` : `${baseURL}/api/v4`,
22
+ timeout: HTTP_TIMEOUT_MS,
19
23
  headers: {
20
24
  'Authorization': `Bearer ${token}`,
21
25
  'Content-Type': 'application/json'
@@ -23,24 +27,110 @@ class GitLabClient {
23
27
  });
24
28
  }
25
29
 
26
- async getGroupProjects(groupId) {
30
+ // List all projects in a group + all its subgroups using BFS.
31
+ async getGroupProjects(groupId, { maxProjects = 1000, maxRequests = 2000 } = {}) {
32
+ let requestCount = 0;
27
33
  const projects = [];
28
- let page = 1;
29
- let hasMore = true;
30
-
31
- while (hasMore) {
32
- const response = await this.client.get(`/groups/${groupId}/projects`, {
33
- params: { page, per_page: 100, include_subgroups: true }
34
- });
35
-
36
- projects.push(...response.data);
37
- hasMore = response.data.length === 100;
38
- page++;
34
+ const toVisit = [groupId];
35
+ const visited = new Set();
36
+
37
+ console.log(
38
+ `[DEBUG] Starting BFS project listing from group ${groupId} (maxProjects=${maxProjects}, maxRequests=${maxRequests})`
39
+ );
40
+
41
+ while (toVisit.length > 0) {
42
+ const currentGroupId = toVisit.shift();
43
+ if (visited.has(currentGroupId)) continue;
44
+ visited.add(currentGroupId);
45
+
46
+ console.log(`[DEBUG] Visiting group ${currentGroupId}. Remaining groups in queue: ${toVisit.length}`);
47
+
48
+ // List projects for THIS group (no include_subgroups!)
49
+ let projPage = 1;
50
+ let hasMoreProjects = true;
51
+
52
+ while (hasMoreProjects) {
53
+ if (requestCount >= maxRequests || projects.length >= maxProjects) {
54
+ console.warn(`[WARN] Stopping project traversal: requestCount=${requestCount}, projects=${projects.length}`);
55
+ return projects;
56
+ }
57
+
58
+ const projRes = await this.getWithRetry(
59
+ `/groups/${currentGroupId}/projects`,
60
+ { page: projPage, per_page: 100 }
61
+ );
62
+
63
+ requestCount++;
64
+ const pageProjects = projRes.data || [];
65
+ if (pageProjects.length > 0) {
66
+ projects.push(...pageProjects);
67
+ }
68
+
69
+ hasMoreProjects = pageProjects.length === 100;
70
+ projPage++;
71
+ }
72
+
73
+ // List DIRECT subgroups and enqueue them
74
+ let subgroupPage = 1;
75
+ let hasMoreSubgroups = true;
76
+
77
+ while (hasMoreSubgroups) {
78
+ if (requestCount >= maxRequests) {
79
+ console.warn(
80
+ `[WARN] Stopping subgroup traversal: requestCount=${requestCount}`
81
+ );
82
+ return projects;
83
+ }
84
+
85
+ const subgroupRes = await this.getWithRetry(
86
+ `/groups/${currentGroupId}/subgroups`,
87
+ { page: subgroupPage, per_page: 100 }
88
+ );
89
+
90
+ requestCount++;
91
+ const subgroups = subgroupRes.data || [];
92
+
93
+ if (subgroups.length > 0) {
94
+ for (const sg of subgroups) {
95
+ if (!visited.has(sg.id)) {
96
+ toVisit.push(sg.id);
97
+ }
98
+ }
99
+ }
100
+
101
+ hasMoreSubgroups = subgroups.length === 100;
102
+ subgroupPage++;
103
+ }
39
104
  }
40
-
105
+
106
+ console.log(`[DEBUG] Finished BFS project listing. Total projects=${projects.length}, total requests=${requestCount}`);
41
107
  return projects;
42
108
  }
43
-
109
+
110
+ // Helper: GET with retry for flaky 5xx/520 errors (Cloudflare / origin issues)
111
+ async getWithRetry(path, params = {}, { retries = 3, retryDelayMs = 2000 } = {}) {
112
+ let lastError;
113
+ for (let attempt = 1; attempt <= retries; attempt++) {
114
+ try {
115
+ return await this.client.get(path, { params });
116
+ } catch (error) {
117
+ const status = error.response?.status;
118
+
119
+ if (attempt < retries && status && status >= 500) {
120
+ console.warn(
121
+ `[WARN] GET ${path} failed with status ${status} (attempt ${attempt}/${retries}). Retrying...`
122
+ );
123
+ await new Promise(resolve => setTimeout(resolve, retryDelayMs * attempt));
124
+ lastError = error;
125
+ continue;
126
+ }
127
+
128
+ throw error; // Non-5xx or out of retries: rethrow
129
+ }
130
+ }
131
+ throw lastError;
132
+ }
133
+
44
134
  async getGroup(groupId) {
45
135
  const response = await this.client.get(`/groups/${groupId}`);
46
136
  return response.data;
@@ -126,7 +216,7 @@ async function promptUser(name) {
126
216
 
127
217
  const answer = await rl.question(`Your new group name is ${name}. Are you sure? (Yes/No)`);
128
218
 
129
- rl.close();
219
+ rl.close();
130
220
 
131
221
  if (answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') {
132
222
  console.log("Proceeding...");
@@ -144,6 +234,39 @@ function validateAndConvertRegion(region) {
144
234
  return `https://${region}.git.cloud.ibm.com/`;
145
235
  }
146
236
 
237
+ // Build a mapping of: old http_url_to_repo -> new http_url_to_repo and old web_url -> new web_url
238
+ async function generateUrlMappingFile({sourceUrl, destUrl, sourceGroup, destinationGroupPath, sourceProjects}) {
239
+ const destBase = destUrl.endsWith('/') ? destUrl.slice(0, -1) : destUrl;
240
+ const urlMapping = {};
241
+
242
+ const groupPrefix = `${sourceGroup.full_path}/`;
243
+
244
+ for (const project of sourceProjects) {
245
+ const oldRepoUrl = project.http_url_to_repo; // ends with .git
246
+
247
+ // path_with_namespace is like "group/subgroup/project-1"
248
+ let relativePath;
249
+ if (project.path_with_namespace.startsWith(groupPrefix)) {
250
+ relativePath = project.path_with_namespace.slice(groupPrefix.length);
251
+ } else {
252
+ // Fallback if for some reason full_path is not a prefix
253
+ relativePath = project.path_with_namespace;
254
+ }
255
+
256
+ const newRepoUrl = `${destBase}/${destinationGroupPath}/${relativePath}.git`;
257
+ urlMapping[oldRepoUrl] = newRepoUrl;
258
+ }
259
+
260
+ const mappingFile = 'grit-url-map.json';
261
+
262
+ await writeFile(mappingFile, JSON.stringify(urlMapping, null, 2), {
263
+ encoding: 'utf8',
264
+ });
265
+
266
+ console.log(`\nURL mapping JSON generated at: ${mappingFile}`);
267
+ console.log(`Total mapped projects: ${sourceProjects.length}`);
268
+ }
269
+
147
270
  async function directTransfer(options) {
148
271
  const sourceUrl = validateAndConvertRegion(options.sourceRegion);
149
272
  const destUrl = validateAndConvertRegion(options.destRegion);
@@ -168,6 +291,15 @@ async function directTransfer(options) {
168
291
  await promptUser(options.newName);
169
292
  }
170
293
 
294
+ // Generate URL mapping JSON before starting the migration
295
+ await generateUrlMappingFile({
296
+ sourceUrl,
297
+ destUrl,
298
+ sourceGroup,
299
+ destinationGroupPath,
300
+ sourceProjects,
301
+ });
302
+
171
303
  let bulkImport = null;
172
304
 
173
305
  const requestPayload = {
@@ -181,10 +313,10 @@ async function directTransfer(options) {
181
313
  destination_slug: destinationGroupPath,
182
314
  destination_namespace: ""
183
315
  }]
184
- }
316
+ };
185
317
 
186
318
  let importRes = null;
187
-
319
+
188
320
  try {
189
321
  importRes = await destination.bulkImport(requestPayload);
190
322
  if (importRes.success) {
@@ -192,9 +324,9 @@ async function directTransfer(options) {
192
324
  console.log(`Bulk import request succeeded!`);
193
325
  console.log(`Bulk import initiated successfully (ID: ${importRes.data?.id})`);
194
326
  } else if (importRes.conflict) {
195
- console.log(`Conflict detected: ${importRes.error}`);
196
- console.log(`Please specify a new group name using -n, --new-name <n> when trying again`);
197
- process.exit(0);
327
+ console.log(`Conflict detected: ${importRes.error}`);
328
+ console.log(`Please specify a new group name using -n, --new-name <n> when trying again`);
329
+ process.exit(0);
198
330
  }
199
331
  } catch (error) {
200
332
  console.log(`Bulk import request failed - ${error.message}`);
@@ -204,11 +336,11 @@ async function directTransfer(options) {
204
336
  console.log('\nPolling bulk import status (checking every 5 minute)...');
205
337
  let importStatus = 'created';
206
338
  let attempts = 0;
207
-
339
+
208
340
  while (!['finished', 'failed', 'timeout'].includes(importStatus) && attempts < 60) {
209
341
  if (attempts > 0) {
210
342
  console.log(`Waiting 5 minute before next status check...`);
211
- await new Promise(resolve => setTimeout(resolve, 5*60000));
343
+ await new Promise(resolve => setTimeout(resolve, 5 * 60000));
212
344
  }
213
345
  try {
214
346
  const importDetails = await destination.getBulkImport(bulkImport.id);
@@ -230,7 +362,7 @@ async function directTransfer(options) {
230
362
  }
231
363
  attempts++;
232
364
  }
233
-
365
+
234
366
  if (attempts >= 60) {
235
367
  console.error(`Bulk import either timed out or is still running in the background`);
236
368
  process.exit(0);
@@ -239,20 +371,20 @@ async function directTransfer(options) {
239
371
  const entities = await destination.getBulkImportEntities(bulkImport.id);
240
372
  const finishedEntities = entities.filter(e => e.status === 'finished');
241
373
  const failedEntities = entities.filter(e => e.status === 'failed');
242
-
374
+
243
375
  if (importStatus === 'finished' && finishedEntities.length > 0) {
244
376
  console.log(`\nGroup migration completed successfully!`);
245
377
  console.log(`Migration Results:`);
246
378
  console.log(`Successfully migrated: ${finishedEntities.length} entities`);
247
379
  console.log(`Failed: ${failedEntities.length} entities`);
248
-
380
+
249
381
  if (failedEntities.length > 0) {
250
382
  console.log(`\nFailed entities:\n`);
251
383
  failedEntities.forEach(e => {
252
384
  console.log(`${e.source_type}: ${e.source_full_path} (${e.status})`);
253
385
  });
254
386
  }
255
-
387
+
256
388
  return 0;
257
389
  } else {
258
390
  console.error('\nBulk import failed!');
@@ -282,7 +414,7 @@ const command = new Command('copy-project-group')
282
414
  .showHelpAfterError()
283
415
  .hook('preAction', cmd => cmd.showHelpAfterError(false)) // only show help during validation
284
416
  .action(async (options) => {
285
- await directTransfer(options);
417
+ await directTransfer(options);
286
418
  });
287
419
 
288
420
  export default command;
@@ -192,7 +192,7 @@ async function setupTerraformFiles({ token, srcRegion, targetRegion, targetTag,
192
192
  // prompt user
193
193
  const validateGritUrlPrompt = async (str) => {
194
194
  if (!str) {
195
- logger.print('Skipping... (URL will remain unchanged in the generatedTerraform configuration)');
195
+ logger.print('Skipping... (URL will remain unchanged in the generated Terraform configuration)');
196
196
  return '';
197
197
  }
198
198
  const newUrl = (GIT_BASE_URL || `https://${targetRegion}.git.cloud.ibm.com`) + `/${str}.git`;
@@ -492,6 +492,7 @@ function replaceDependsOn(str) {
492
492
  function addS2sScriptToToolchainTf(str) {
493
493
  const provisionerStr = (tfName) => `\n\n provisioner "local-exec" {
494
494
  command = "node create-s2s-script.cjs"
495
+ on_failure = fail
495
496
  environment = {
496
497
  IBMCLOUD_API_KEY = var.ibmcloud_api_key
497
498
  TARGET_TOOLCHAIN_ID = ibm_cd_toolchain.${tfName}.id
package/config.js CHANGED
@@ -11,9 +11,9 @@ const COPY_TOOLCHAIN_DESC = `Copies a toolchain, including tool integrations and
11
11
 
12
12
  Examples:
13
13
  export IBMCLOUD_API_KEY='...'
14
- npx @ibm-cloud/cd-migration-tools copy-toolchain -c \${TOOLCHAIN_CRN} -r us-south
14
+ npx @ibm-cloud/cd-tools copy-toolchain -c \${TOOLCHAIN_CRN} -r us-south
15
15
  Copy a toolchain to the Dallas region with the same name, in the same resource group.
16
- npx @ibm-cloud/cd-migration-tools copy-toolchain -c \${TOOLCHAIN_CRN} -r eu-de -n new-toolchain-name -g new-resource-group --apikey \${APIKEY}
16
+ npx @ibm-cloud/cd-tools copy-toolchain -c \${TOOLCHAIN_CRN} -r eu-de -n new-toolchain-name -g new-resource-group --apikey \${APIKEY}
17
17
  Copy a toolchain to the Frankfurt region with the specified name and target resource group, using the given API key
18
18
 
19
19
  Environment Variables:
@@ -9,6 +9,7 @@
9
9
 
10
10
  const fs = require('node:fs');
11
11
  const { resolve } = require('node:path');
12
+ const { exit } = require('node:process');
12
13
 
13
14
  const API_KEY = process.env['IBMCLOUD_API_KEY'];
14
15
  if (!API_KEY) throw Error(`Missing 'IBMCLOUD_API_KEY'`);
@@ -103,21 +104,29 @@ async function createS2sAuthPolicy(bearer, item) {
103
104
  });
104
105
 
105
106
  if (!response.ok) {
106
- throw new Error(`Response status: ${response.status}, ${response.statusText}`);
107
+ return Promise.reject(`Failed to create service-to-service authorization policy for ${item['serviceId']} '${item['parameters']['label'] ?? item['parameters']['name']}' with status: ${response.status} ${response.statusText}`);
107
108
  }
108
109
 
109
110
  console.log(`CREATING AUTH POLICY... ${response.status}, ${response.statusText}`);
110
111
  } catch (error) {
111
- console.error(error.message);
112
+ return Promise.reject(error.message);
112
113
  }
113
114
  }
114
115
 
115
116
  // main
116
117
 
117
- getBearer().then((bearer) => {
118
+ getBearer().then(async (bearer) => {
118
119
  const inputArr = JSON.parse(fs.readFileSync(resolve(INPUT_PATH)));
119
120
 
120
- inputArr.forEach(async (item) => {
121
- await createS2sAuthPolicy(bearer, item);
121
+ const promises = [];
122
+ inputArr.forEach((item) => {
123
+ promises.push(createS2sAuthPolicy(bearer, item));
122
124
  });
125
+
126
+ try {
127
+ await Promise.all(promises);
128
+ } catch (e) {
129
+ console.error(e)
130
+ exit(1);
131
+ }
123
132
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibm-cloud/cd-tools",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Tools and utilities for the IBM Cloud Continuous Delivery service and resources",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,10 +37,19 @@ describe('copy-toolchain: Test functionalities', function () {
37
37
  const testCases = [
38
38
  {
39
39
  name: 'Terraform Version Verification',
40
- cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
40
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region],
41
41
  expected: /✔ Terraform Version:/,
42
42
  options: {
43
- exitCondition: '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):',
43
+ exitCondition: `(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['empty'].name}] (Ctrl-C to abort):`,
44
+ timeout: 10000
45
+ }
46
+ },
47
+ {
48
+ name: 'CLI Version Verification',
49
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region],
50
+ expected: /✔ cd-tools Version:/,
51
+ options: {
52
+ exitCondition: `(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['empty'].name}] (Ctrl-C to abort):`,
44
53
  timeout: 10000
45
54
  }
46
55
  },
@@ -56,10 +65,10 @@ describe('copy-toolchain: Test functionalities', function () {
56
65
  },
57
66
  {
58
67
  name: 'Log file is created successfully',
59
- cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
68
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region],
60
69
  expected: null,
61
70
  options: {
62
- exitCondition: '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):',
71
+ exitCondition: `(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['empty'].name}] (Ctrl-C to abort):`,
63
72
  timeout: 10000,
64
73
  cwd: TEMP_DIR + '/' + 'log-file-is-created-successfully'
65
74
  },
@@ -81,15 +90,6 @@ describe('copy-toolchain: Test functionalities', function () {
81
90
  assert.isTrue(toolchainData.id === toolchainId, 'Was toolchain created successfully without any confirmations?');
82
91
  }
83
92
  },
84
- {
85
- name: 'Prompt User when toolchain name already exists in resource group',
86
- cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
87
- expected: new RegExp(`Warning! A toolchain named \'${TEST_TOOLCHAINS['empty'].name}\' already exists in:[\\s\\S]*?Resource Group:[\\s\\S]*?${R2R_CLI_RG_ID}`),
88
- options: {
89
- exitCondition: '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):',
90
- timeout: 10000
91
- }
92
- },
93
93
  {
94
94
  name: 'Prompt User when toolchain name already exists in region',
95
95
  cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region, '-g', DEFAULT_RG_ID],
@@ -125,10 +125,13 @@ describe('copy-toolchain: Test user input handling', function () {
125
125
  const invalidUserInputCases = [
126
126
  {
127
127
  name: 'Invalid Toolchain tag is provided',
128
- cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
128
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region],
129
129
  expected: /Provided tag is invalid/,
130
130
  options: {
131
- questionAnswerMap: { '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': mocks.invalidTag },
131
+ questionAnswerMap: {
132
+ [`(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['empty'].name}] (Ctrl-C to abort):`]: '',
133
+ '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': mocks.invalidTag
134
+ },
132
135
  exitCondition: 'Validation failed',
133
136
  timeout: 10000
134
137
  }
@@ -43,12 +43,12 @@ describe('copy-toolchain: Test import-terraform output', function () {
43
43
  ibm_cd_tekton_pipeline_trigger: 1,
44
44
  ibm_cd_tekton_pipeline_trigger_property: 1,
45
45
  ibm_cd_toolchain: 1,
46
+ ibm_cd_toolchain_tool_cos: 1,
46
47
  ibm_cd_toolchain_tool_custom: 1,
47
48
  ibm_cd_toolchain_tool_githubconsolidated: 1,
48
49
  ibm_cd_toolchain_tool_pipeline: 1,
49
50
  ibm_cd_toolchain_tool_secretsmanager: 1,
50
51
  ibm_cd_toolchain_tool_slack: 1,
51
- ibm_iam_authorization_policy: 1
52
52
  });
53
53
  }
54
54
  },
@@ -68,12 +68,12 @@ describe('copy-toolchain: Test import-terraform output', function () {
68
68
  ibm_cd_tekton_pipeline_trigger: 1,
69
69
  ibm_cd_tekton_pipeline_trigger_property: 1,
70
70
  ibm_cd_toolchain: 1,
71
+ ibm_cd_toolchain_tool_cos: 1,
71
72
  ibm_cd_toolchain_tool_custom: 1,
72
73
  ibm_cd_toolchain_tool_githubconsolidated: 1,
73
74
  ibm_cd_toolchain_tool_pipeline: 1,
74
75
  ibm_cd_toolchain_tool_secretsmanager: 1,
75
76
  ibm_cd_toolchain_tool_slack: 1,
76
- ibm_iam_authorization_policy: 1
77
77
  });
78
78
  }
79
79
  },
@@ -99,7 +99,6 @@ describe('copy-toolchain: Test import-terraform output', function () {
99
99
  ibm_cd_toolchain_tool_pipeline: 1,
100
100
  ibm_cd_toolchain_tool_secretsmanager: 1,
101
101
  ibm_cd_toolchain_tool_slack: 1,
102
- ibm_iam_authorization_policy: 1
103
102
  })
104
103
  }
105
104
  },
@@ -56,7 +56,6 @@ describe('copy-toolchain: Test tool validation', function () {
56
56
  },
57
57
  assertionFunc: (output) => {
58
58
  expect(output).to.match(/Warning! The following tools contain secrets that cannot be migrated/);
59
- expect(output).to.match(/cloudobjectstorage[\s\S]*?cos_api_key/);
60
59
  expect(output).to.match(/slack[\s\S]*?api_token/);
61
60
  expect(output).to.match(/pipeline[\s\S]*?properties.doi-ibmcloud-api-key/);
62
61
  }