Skip to content

Commit a1321cf

Browse files
committed
Merge branch 'bugfix/CLDSRV-758' into q/9.1
2 parents 3d7d86a + 3f8ed81 commit a1321cf

3 files changed

Lines changed: 686 additions & 0 deletions

File tree

lib/routes/veeam/get.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,25 @@ function getVeeamFile(request, response, bucketMd, log) {
5757
const bucketKey = `${bucketMd._name}_${new Date(bucketMd._creationDate).getTime()}`;
5858
return UtilizationService.getUtilizationMetrics('bucket', bucketKey, null, {}, (err, bucketMetrics) => {
5959
if (err) {
60+
// Handle errors from UtilizationService/scubaclient
61+
// axios errors have status in err.response.status
62+
const statusCode = err.response?.status || err.statusCode || err.code;
63+
// Only handle 404 gracefully (no metrics available yet, e.g. post-install)
64+
// For 404, continue with static capacity data (Used=0 from bucket metadata)
65+
if (statusCode === 404) {
66+
log.warn('UtilizationService returned 404 when fetching capacity metrics', {
67+
method: 'getVeeamFile',
68+
bucket: request.bucketName,
69+
error: err.message || err.code,
70+
});
71+
return finalizeRequest();
72+
}
73+
log.error('error fetching capacity metrics from UtilizationService', {
74+
method: 'getVeeamFile',
75+
bucket: request.bucketName,
76+
error: err.message || err.code,
77+
statusCode,
78+
});
6079
return responseXMLBody(errors.InternalError, null, response, log);
6180
}
6281
return finalizeRequest(bucketMetrics);

tests/sur/routeVeeam.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,111 @@ function makeVeeamRequest(params, callback) {
383383
return done();
384384
}));
385385
});
386+
387+
it('GET capacity.xml should return 200 when scubaclient returns 404 (post-install scenario)', done => {
388+
// This test simulates the post-install scenario where scubaclient returns 404
389+
// because no metrics are available yet. By not calling scuba.incrementBytesForBucket,
390+
// the mock scuba server will return 404 for this bucket.
391+
392+
async.waterfall([
393+
next => makeVeeamRequest({
394+
method: 'PUT',
395+
bucket: TEST_BUCKET,
396+
objectKey: '.system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/capacity.xml',
397+
headers: {
398+
'content-length': testCapacity.length,
399+
'content-md5': testCapacityMd5,
400+
'x-scal-canonical-id': testArn,
401+
},
402+
authCredentials: veeamAuthCredentials,
403+
requestBody: testCapacity,
404+
}, (err, response) => {
405+
if (err) {
406+
return done(err);
407+
}
408+
assert.strictEqual(response.statusCode, 200);
409+
return next();
410+
}),
411+
next => makeVeeamRequest({
412+
method: 'GET',
413+
bucket: TEST_BUCKET,
414+
objectKey: '.system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/capacity.xml',
415+
headers: {
416+
'x-scal-canonical-id': testArn,
417+
},
418+
authCredentials: veeamAuthCredentials,
419+
}, (err, response) => {
420+
if (err) {
421+
return done(err);
422+
}
423+
// Critical assertion: for 404 from scubaclient (no metrics yet),
424+
// should return 200 with static capacity data (Used=0)
425+
assert.strictEqual(response.statusCode, 200,
426+
'should return 200 when scubaclient returns 404 (no metrics available)');
427+
// Should return capacity.xml with static data
428+
assert(response.body.includes('<CapacityInfo>'),
429+
'should return capacity.xml content');
430+
assert(response.body.includes('<Used>0</Used>'),
431+
'Used should be 0 from static bucket metadata');
432+
return next();
433+
}),
434+
], err => {
435+
assert.ifError(err);
436+
return done();
437+
});
438+
});
439+
440+
it('GET system.xml should return 200 even when scubaclient is down', done => {
441+
// system.xml doesn't use scubaclient, so it should always work
442+
// This test stops scuba to verify system.xml is independent of utilization metrics
443+
async.waterfall([
444+
next => makeVeeamRequest({
445+
method: 'PUT',
446+
bucket: TEST_BUCKET,
447+
objectKey: '.system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/system.xml',
448+
headers: {
449+
'content-length': testSystem.length,
450+
'content-md5': testSystemMd5,
451+
'x-scal-canonical-id': testArn,
452+
},
453+
authCredentials: veeamAuthCredentials,
454+
requestBody: testSystem,
455+
}, (err, response) => {
456+
if (err) {
457+
return done(err);
458+
}
459+
assert.strictEqual(response.statusCode, 200);
460+
return next();
461+
}),
462+
next => {
463+
// Stop scuba - system.xml should still work
464+
scuba.stop();
465+
return next();
466+
},
467+
next => makeVeeamRequest({
468+
method: 'GET',
469+
bucket: TEST_BUCKET,
470+
objectKey: '.system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/system.xml',
471+
headers: {
472+
'x-scal-canonical-id': testArn,
473+
},
474+
authCredentials: veeamAuthCredentials,
475+
}, (err, response) => {
476+
if (err) {
477+
return done(err);
478+
}
479+
assert.strictEqual(response.statusCode, 200,
480+
'system.xml should always return 200 even when scuba is down');
481+
assert.strictEqual(response.body.replaceAll(' ', ''), testSystem.replaceAll(' ', ''));
482+
return next();
483+
}),
484+
], err => {
485+
// Restart scuba for subsequent tests
486+
scuba.start();
487+
assert.ifError(err);
488+
return done();
489+
});
490+
});
386491
});
387492

388493
describe('veeam DELETE routes:', () => {

0 commit comments

Comments
 (0)