@gcnv/gcnv-mcp-server 1.0.3
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/GEMINI.md +200 -0
- package/LICENSE +201 -0
- package/README.md +374 -0
- package/build/index.js +185 -0
- package/build/logger.js +19 -0
- package/build/registry/register-tools.js +101 -0
- package/build/registry/tool-registry.js +27 -0
- package/build/tools/active-directory-tools.js +124 -0
- package/build/tools/backup-policy-tools.js +140 -0
- package/build/tools/backup-tools.js +178 -0
- package/build/tools/backup-vault-tools.js +147 -0
- package/build/tools/handlers/active-directory-handler.js +321 -0
- package/build/tools/handlers/backup-handler.js +451 -0
- package/build/tools/handlers/backup-policy-handler.js +275 -0
- package/build/tools/handlers/backup-vault-handler.js +370 -0
- package/build/tools/handlers/kms-config-handler.js +327 -0
- package/build/tools/handlers/operation-handler.js +254 -0
- package/build/tools/handlers/quota-rule-handler.js +411 -0
- package/build/tools/handlers/replication-handler.js +504 -0
- package/build/tools/handlers/snapshot-handler.js +320 -0
- package/build/tools/handlers/storage-pool-handler.js +346 -0
- package/build/tools/handlers/volume-handler.js +353 -0
- package/build/tools/kms-config-tools.js +162 -0
- package/build/tools/operation-tools.js +64 -0
- package/build/tools/quota-rule-tools.js +166 -0
- package/build/tools/replication-tools.js +227 -0
- package/build/tools/snapshot-tools.js +124 -0
- package/build/tools/storage-pool-tools.js +215 -0
- package/build/tools/volume-tools.js +216 -0
- package/build/types/tool.js +1 -0
- package/build/utils/netapp-client-factory.js +53 -0
- package/gemini-extension.json +12 -0
- package/package.json +66 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { NetAppClientFactory } from '../../utils/netapp-client-factory.js';
|
|
2
|
+
import { logger } from '../../logger.js';
|
|
3
|
+
const log = logger.child({ module: 'backup-handler' });
|
|
4
|
+
// Helper to format backup data for responses
|
|
5
|
+
function formatBackupData(backup) {
|
|
6
|
+
const result = {};
|
|
7
|
+
if (!backup)
|
|
8
|
+
return result;
|
|
9
|
+
if (backup.name) {
|
|
10
|
+
// Extract backupId from name (last part after last slash)
|
|
11
|
+
const nameParts = backup.name.split('/');
|
|
12
|
+
result.name = backup.name;
|
|
13
|
+
result.backupId = nameParts[nameParts.length - 1];
|
|
14
|
+
// Extract backupVaultId from name
|
|
15
|
+
const backupVaultMatch = backup.name.match(/\/backupVaults\/([^/]+)\/backups\//);
|
|
16
|
+
if (backupVaultMatch && backupVaultMatch[1]) {
|
|
17
|
+
result.backupVaultId = backupVaultMatch[1];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// Map source volume
|
|
21
|
+
if (backup.sourceVolume) {
|
|
22
|
+
result.sourceVolume = backup.sourceVolume; // Map sourceName to sourceVolume for schema consistency
|
|
23
|
+
}
|
|
24
|
+
// Copy basic properties
|
|
25
|
+
if (backup.state)
|
|
26
|
+
result.state = backup.state;
|
|
27
|
+
// Map volume usage bytes
|
|
28
|
+
result.volumeUsagebytes = backup.volumeUsagebytes; // Keep original for compatibility
|
|
29
|
+
// Format timestamps if they exist
|
|
30
|
+
if (backup.createTime) {
|
|
31
|
+
result.createTime = new Date(backup.createTime.seconds * 1000);
|
|
32
|
+
}
|
|
33
|
+
// Copy optional properties according to schema
|
|
34
|
+
if (backup.description)
|
|
35
|
+
result.description = backup.description;
|
|
36
|
+
if (backup.backupType)
|
|
37
|
+
result.backupType = backup.backupType;
|
|
38
|
+
result.chainStoragebytes = backup.chainStoragebytes || 0;
|
|
39
|
+
if (backup.satisfiesPzs !== undefined)
|
|
40
|
+
result.satisfiesPzs = backup.satisfiesPzs;
|
|
41
|
+
if (backup.satisfiesPzi !== undefined)
|
|
42
|
+
result.satisfiesPzi = backup.satisfiesPzi;
|
|
43
|
+
if (backup.volumeRegion)
|
|
44
|
+
result.volumeRegion = backup.volumeRegion;
|
|
45
|
+
if (backup.backupRegion)
|
|
46
|
+
result.backupRegion = backup.backupRegion;
|
|
47
|
+
if (backup.enforcedRetentionEndTime)
|
|
48
|
+
result.enforcedRetentionEndTime = backup.enforcedRetentionEndTime;
|
|
49
|
+
result.sourceSnapshot = backup.sourceSnapshot;
|
|
50
|
+
if (backup.labels)
|
|
51
|
+
result.labels = backup.labels;
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
// Create Backup Handler
|
|
55
|
+
export const createBackupHandler = async (args) => {
|
|
56
|
+
try {
|
|
57
|
+
const { projectId, location, backupVaultId, backupId, sourceVolumeName, backupRegion, enforcedRetentionEndTime, description, labels, } = args;
|
|
58
|
+
// Create a new NetApp client using the factory
|
|
59
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
60
|
+
// Format the parent path for the backup
|
|
61
|
+
const parent = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}`;
|
|
62
|
+
// Construct the backup name
|
|
63
|
+
const backupName = `${parent}/backups/${backupId}`;
|
|
64
|
+
// Create the backup request
|
|
65
|
+
const request = {
|
|
66
|
+
parent,
|
|
67
|
+
backupId,
|
|
68
|
+
backup: {
|
|
69
|
+
name: backupName,
|
|
70
|
+
sourceVolume: sourceVolumeName,
|
|
71
|
+
backupRegion,
|
|
72
|
+
enforcedRetentionEndTime,
|
|
73
|
+
description,
|
|
74
|
+
labels,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
log.info({ request }, 'Create Backup request');
|
|
78
|
+
// Create the backup
|
|
79
|
+
const [operation] = await netAppClient.createBackup(request);
|
|
80
|
+
// Extract the operation name for tracking
|
|
81
|
+
const operationName = operation.name;
|
|
82
|
+
log.info({ operationName }, 'Backup creation operation started');
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: `${operationName}`,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
structuredContent: {
|
|
91
|
+
name: backupName,
|
|
92
|
+
operationId: operationName,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
log.error({ err: error }, 'Error creating backup');
|
|
98
|
+
let errorMessage = `Failed to create backup: ${error.message}`;
|
|
99
|
+
// Handle specific error types and provide useful error messages
|
|
100
|
+
if (error.code === 6) {
|
|
101
|
+
// ALREADY_EXISTS
|
|
102
|
+
errorMessage = `Backup ${args.backupId} already exists in backup vault ${args.backupVaultId}`;
|
|
103
|
+
}
|
|
104
|
+
else if (error.code === 7) {
|
|
105
|
+
// PERMISSION_DENIED
|
|
106
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
107
|
+
}
|
|
108
|
+
else if (error.code === 5) {
|
|
109
|
+
// NOT_FOUND
|
|
110
|
+
errorMessage = `Backup vault or volume not found`;
|
|
111
|
+
}
|
|
112
|
+
else if (error.code === 3) {
|
|
113
|
+
// INVALID_ARGUMENT
|
|
114
|
+
errorMessage = `Invalid argument: ${error.message}`;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
isError: true,
|
|
118
|
+
content: [
|
|
119
|
+
{
|
|
120
|
+
type: 'text',
|
|
121
|
+
text: errorMessage,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
// Delete Backup Handler
|
|
128
|
+
export const deleteBackupHandler = async (args) => {
|
|
129
|
+
try {
|
|
130
|
+
const { projectId, location, backupVaultId, backupId } = args;
|
|
131
|
+
// Create a new NetApp client using the factory
|
|
132
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
133
|
+
// Format the name for the backup
|
|
134
|
+
const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}/backups/${backupId}`;
|
|
135
|
+
// Delete the backup
|
|
136
|
+
const [operation] = await netAppClient.deleteBackup({
|
|
137
|
+
name,
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
content: [
|
|
141
|
+
{
|
|
142
|
+
type: 'text',
|
|
143
|
+
text: `Backup deletion initiated. Operation ID: ${operation.name}`,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
structuredContent: {
|
|
147
|
+
success: true,
|
|
148
|
+
operationId: operation.name,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
log.error({ err: error }, 'Error deleting backup');
|
|
154
|
+
let errorMessage = `Failed to delete backup: ${error.message}`;
|
|
155
|
+
// Handle specific error types
|
|
156
|
+
if (error.code === 5) {
|
|
157
|
+
// NOT_FOUND
|
|
158
|
+
errorMessage = `Backup not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}/backups/${args.backupId}`;
|
|
159
|
+
}
|
|
160
|
+
else if (error.code === 7) {
|
|
161
|
+
// PERMISSION_DENIED
|
|
162
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
isError: true,
|
|
166
|
+
content: [
|
|
167
|
+
{
|
|
168
|
+
type: 'text',
|
|
169
|
+
text: errorMessage,
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
// Get Backup Handler
|
|
176
|
+
export const getBackupHandler = async (args) => {
|
|
177
|
+
try {
|
|
178
|
+
const { projectId, location, backupVaultId, backupId } = args;
|
|
179
|
+
// Create a new NetApp client using the factory
|
|
180
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
181
|
+
// Format the name for the backup
|
|
182
|
+
const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}/backups/${backupId}`;
|
|
183
|
+
// Get the backup
|
|
184
|
+
const [backup] = await netAppClient.getBackup({ name });
|
|
185
|
+
log.info({ backup }, 'Raw backup data');
|
|
186
|
+
// Format the backup data
|
|
187
|
+
const formattedData = formatBackupData(backup);
|
|
188
|
+
log.info({ formattedData }, 'Formatted backup data');
|
|
189
|
+
// Ensure all required fields are present
|
|
190
|
+
if (!formattedData.state)
|
|
191
|
+
formattedData.state = 'UNKNOWN';
|
|
192
|
+
if (!formattedData.sourceVolume) {
|
|
193
|
+
// Create a default source volume name based on the backup name pattern
|
|
194
|
+
formattedData.sourceVolume = `projects/${projectId}/locations/${location}/storagePools/unknown/volumes/unknown`;
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: 'text',
|
|
200
|
+
text: JSON.stringify(formattedData, null, 2),
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
structuredContent: formattedData,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
log.error({ err: error }, 'Error getting backup');
|
|
208
|
+
let errorMessage = `Failed to get backup: ${error.message}`;
|
|
209
|
+
// Handle specific error types
|
|
210
|
+
if (error.code === 5) {
|
|
211
|
+
// NOT_FOUND
|
|
212
|
+
errorMessage = `Backup not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}/backups/${args.backupId}`;
|
|
213
|
+
}
|
|
214
|
+
else if (error.code === 7) {
|
|
215
|
+
// PERMISSION_DENIED
|
|
216
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
isError: true,
|
|
220
|
+
content: [
|
|
221
|
+
{
|
|
222
|
+
type: 'text',
|
|
223
|
+
text: errorMessage,
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
// List Backups Handler
|
|
230
|
+
export const listBackupsHandler = async (args) => {
|
|
231
|
+
try {
|
|
232
|
+
const { projectId, location, backupVaultId, filter, pageSize, pageToken } = args;
|
|
233
|
+
// Create a new NetApp client using the factory
|
|
234
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
235
|
+
// Format the parent path for listing backups
|
|
236
|
+
const parent = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}`;
|
|
237
|
+
// List the backups
|
|
238
|
+
const options = {
|
|
239
|
+
parent,
|
|
240
|
+
filter,
|
|
241
|
+
pageSize,
|
|
242
|
+
pageToken,
|
|
243
|
+
};
|
|
244
|
+
const [backups, , nextPageToken] = await netAppClient.listBackups(options);
|
|
245
|
+
log.info({ backups }, 'Raw backups data');
|
|
246
|
+
const formattedBackups = backups.map((backup) => formatBackupData(backup));
|
|
247
|
+
log.info({ formattedBackups }, 'Formatted backups data');
|
|
248
|
+
return {
|
|
249
|
+
content: [
|
|
250
|
+
{
|
|
251
|
+
type: 'text',
|
|
252
|
+
text: JSON.stringify(formattedBackups, null, 2),
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
structuredContent: {
|
|
256
|
+
backups: formattedBackups,
|
|
257
|
+
nextPageToken: nextPageToken || undefined,
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
log.error({ err: error }, 'Error listing backups');
|
|
263
|
+
let errorMessage = `Failed to list backups: ${error.message}`;
|
|
264
|
+
// Handle specific error types
|
|
265
|
+
if (error.code === 5) {
|
|
266
|
+
// NOT_FOUND
|
|
267
|
+
errorMessage = `Backup vault not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}`;
|
|
268
|
+
}
|
|
269
|
+
else if (error.code === 7) {
|
|
270
|
+
// PERMISSION_DENIED
|
|
271
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
272
|
+
}
|
|
273
|
+
else if (error.code === 3) {
|
|
274
|
+
// INVALID_ARGUMENT
|
|
275
|
+
errorMessage = `Invalid argument: ${error.message}`;
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
isError: true,
|
|
279
|
+
content: [
|
|
280
|
+
{
|
|
281
|
+
type: 'text',
|
|
282
|
+
text: errorMessage,
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
// Restore Backup Handler
|
|
289
|
+
export const restoreBackupHandler = async (args) => {
|
|
290
|
+
try {
|
|
291
|
+
const { projectId, location, backupVaultId, backupId, targetStoragePoolId, targetVolumeId, restoreOption, } = args;
|
|
292
|
+
// Create a new NetApp client using the factory
|
|
293
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
294
|
+
// Format the name for the backup
|
|
295
|
+
const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}/backups/${backupId}`;
|
|
296
|
+
// Format the target volume name
|
|
297
|
+
const targetVolumeName = `projects/${projectId}/locations/${location}/storagePools/${targetStoragePoolId}/volumes/${targetVolumeId}`;
|
|
298
|
+
// Create restore options
|
|
299
|
+
let requestOptions = {};
|
|
300
|
+
if (restoreOption === 'CREATE_NEW_VOLUME') {
|
|
301
|
+
requestOptions = {
|
|
302
|
+
targetVolumeName: targetVolumeName,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
else if (restoreOption === 'OVERWRITE_EXISTING_VOLUME') {
|
|
306
|
+
requestOptions = {
|
|
307
|
+
targetVolumeName: targetVolumeName,
|
|
308
|
+
overwriteExistingVolume: true,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
// Get the available methods from the client for debugging
|
|
312
|
+
log.debug({
|
|
313
|
+
methods: Object.keys(netAppClient).filter((k) => typeof netAppClient[k] === 'function'),
|
|
314
|
+
}, 'Available NetApp client methods');
|
|
315
|
+
// Attempt to use the backup client - we'll use a safe approach with any to avoid compile errors
|
|
316
|
+
// and log a proper error if the method doesn't exist
|
|
317
|
+
let operation;
|
|
318
|
+
try {
|
|
319
|
+
// Try the method that seems most likely
|
|
320
|
+
const client = netAppClient;
|
|
321
|
+
if (typeof client.restoreBackup === 'function') {
|
|
322
|
+
[operation] = await client.restoreBackup({
|
|
323
|
+
name,
|
|
324
|
+
...requestOptions,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
else if (typeof client.restoreVolumeBackup === 'function') {
|
|
328
|
+
[operation] = await client.restoreVolumeBackup({
|
|
329
|
+
name,
|
|
330
|
+
...requestOptions,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
throw new Error('restoreBackup method not found on NetApp client');
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch (restoreError) {
|
|
338
|
+
log.error({ err: restoreError }, 'Error in restore operation');
|
|
339
|
+
throw restoreError;
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
content: [
|
|
343
|
+
{
|
|
344
|
+
type: 'text',
|
|
345
|
+
text: `Backup restore initiated. Operation ID: ${operation.name}`,
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
structuredContent: {
|
|
349
|
+
name: targetVolumeName,
|
|
350
|
+
operationId: operation.name,
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
log.error({ err: error }, 'Error restoring backup');
|
|
356
|
+
let errorMessage = `Failed to restore backup: ${error.message}`;
|
|
357
|
+
// Handle specific error types
|
|
358
|
+
if (error.code === 5) {
|
|
359
|
+
// NOT_FOUND
|
|
360
|
+
errorMessage = `Backup or target storage pool not found`;
|
|
361
|
+
}
|
|
362
|
+
else if (error.code === 7) {
|
|
363
|
+
// PERMISSION_DENIED
|
|
364
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
365
|
+
}
|
|
366
|
+
else if (error.code === 6) {
|
|
367
|
+
// ALREADY_EXISTS
|
|
368
|
+
errorMessage = `Target volume already exists and overwrite option was not selected`;
|
|
369
|
+
}
|
|
370
|
+
else if (error.code === 9) {
|
|
371
|
+
// FAILED_PRECONDITION
|
|
372
|
+
errorMessage = `Failed precondition: ${error.message}`;
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
isError: true,
|
|
376
|
+
content: [
|
|
377
|
+
{
|
|
378
|
+
type: 'text',
|
|
379
|
+
text: errorMessage,
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
// Update Backup Handler
|
|
386
|
+
export const updateBackupHandler = async (args) => {
|
|
387
|
+
try {
|
|
388
|
+
const { projectId, location, backupVaultId, backupId, description, labels } = args;
|
|
389
|
+
// Create a new NetApp client using the factory
|
|
390
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
391
|
+
// Format the name for the backup
|
|
392
|
+
const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}/backups/${backupId}`;
|
|
393
|
+
// Prepare the update mask based on provided fields
|
|
394
|
+
const updateMask = [];
|
|
395
|
+
const backup = { name };
|
|
396
|
+
if (description !== undefined) {
|
|
397
|
+
backup.description = description;
|
|
398
|
+
updateMask.push('description');
|
|
399
|
+
}
|
|
400
|
+
if (labels !== undefined) {
|
|
401
|
+
backup.labels = labels;
|
|
402
|
+
updateMask.push('labels');
|
|
403
|
+
}
|
|
404
|
+
// Call the API to update the backup
|
|
405
|
+
const request = {
|
|
406
|
+
backup,
|
|
407
|
+
updateMask: {
|
|
408
|
+
paths: updateMask,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
log.info({ request }, 'Update Backup request');
|
|
412
|
+
const [operation] = await netAppClient.updateBackup(request);
|
|
413
|
+
log.info({ operation }, 'Update Backup operation');
|
|
414
|
+
return {
|
|
415
|
+
content: [
|
|
416
|
+
{
|
|
417
|
+
type: 'text',
|
|
418
|
+
text: JSON.stringify({
|
|
419
|
+
message: `Backup '${backupId}' update operation started`,
|
|
420
|
+
operation: operation,
|
|
421
|
+
}, null, 2),
|
|
422
|
+
},
|
|
423
|
+
],
|
|
424
|
+
structuredContent: {
|
|
425
|
+
name: name,
|
|
426
|
+
operationId: operation.name || '',
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
log.error({ err: error }, 'Error updating backup');
|
|
432
|
+
let errorMessage = `Failed to update backup: ${error.message}`;
|
|
433
|
+
if (error.code === 5) {
|
|
434
|
+
// NOT_FOUND
|
|
435
|
+
errorMessage = `Backup not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}/backups/${args.backupId}`;
|
|
436
|
+
}
|
|
437
|
+
else if (error.code === 7) {
|
|
438
|
+
// PERMISSION_DENIED
|
|
439
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
isError: true,
|
|
443
|
+
content: [
|
|
444
|
+
{
|
|
445
|
+
type: 'text',
|
|
446
|
+
text: errorMessage,
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
};
|