Skip to content

Commit d4147d8

Browse files
Merge branch 'improvement/CLDSRV-599' into w/9.2/improvement/CLDSRV-599
2 parents e6f61ec + 15f163f commit d4147d8

File tree

3 files changed

+328
-5
lines changed

3 files changed

+328
-5
lines changed

lib/Config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,6 +1877,14 @@ class Config extends EventEmitter {
18771877
this.rateLimiting = parseRateLimitConfig(config.rateLimiting, this.clusters = this.clusters || 1);
18781878
}
18791879

1880+
1881+
if (config.capabilities) {
1882+
if (config.capabilities.locationTypes) {
1883+
config.capabilities.locationTypes = new Set(config.capabilities.locationTypes);
1884+
}
1885+
this.capabilities = config.capabilities;
1886+
}
1887+
18801888
return config;
18811889
}
18821890

lib/utilities/reportHandler.js

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,50 @@ function hasWSOptionalDependencies() {
3434
}
3535
}
3636

37-
function getCapabilities() {
38-
const localVolumeCap = process.env.LOCAL_VOLUME_CAPABILITY || 'true';
39-
return {
37+
function getCapabilities(cfg = config) {
38+
const caps = cfg.capabilities || {
39+
// Default capabilities, for backward compatibility. Should not be modified,
40+
// changes are expected to be done through config.json file.
41+
locationTypeAzure: true,
42+
locationTypeGCP: true,
4043
locationTypeDigitalOcean: true,
4144
locationTypeS3Custom: true,
4245
locationTypeSproxyd: true,
4346
locationTypeNFS: true,
4447
locationTypeCephRadosGW: true,
4548
locationTypeHyperdriveV2: true,
46-
locationTypeLocal: localVolumeCap === '1' || localVolumeCap.toLowerCase() === 'true',
49+
locationTypeLocal: true,
4750
preferredReadLocation: true,
4851
managedLifecycle: true,
4952
managedLifecycleTransition: true,
50-
secureChannelOptimizedPath: hasWSOptionalDependencies(),
53+
secureChannelOptimizedPath: true,
5154
s3cIngestLocation: true,
5255
nfsIngestLocation: false,
5356
cephIngestLocation: false,
5457
awsIngestLocation: false,
5558
};
59+
60+
// Consistency & safety checks for capabilities that depend on other config values
61+
const localVolumeCap = process.env.LOCAL_VOLUME_CAPABILITY || 'true';
62+
caps.locationTypeLocal &&= (localVolumeCap === '1' || localVolumeCap.toLowerCase() === 'true');
63+
caps.secureChannelOptimizedPath &&= hasWSOptionalDependencies();
64+
caps.managedLifecycle &&= cfg.supportedLifecycleRules.includes('Expiration');
65+
caps.managedLifecycleTransition &&= cfg.supportedLifecycleRules.includes('Transition');
66+
caps.lifecycleRules &&= cfg.supportedLifecycleRules;
67+
68+
// Map locationTypes entries to the respective "legacy" capability flags
69+
if (caps.locationTypes) {
70+
caps.locationTypeAzure &&= caps.locationTypes.includes('location-azure-v1');
71+
caps.locationTypeGCP &&= caps.locationTypes.includes('location-gcp-v1');
72+
caps.locationTypeDigitalOcean &&= caps.locationTypes.includes('location-do-spaces-v1');
73+
caps.locationTypeSproxyd &&= caps.locationTypes.includes('location-scality-sproxyd-v1');
74+
caps.locationTypeNFS &&= caps.locationTypes.includes('location-nfs-mount-v1');
75+
caps.locationTypeCephRadosGW &&= caps.locationTypes.includes('location-ceph-radosgw-s3-v1');
76+
caps.locationTypeHyperdriveV2 &&= caps.locationTypes.includes('location-scality-hdclient-v2');
77+
caps.locationTypeLocal &&= caps.locationTypes.includes('location-file-v1');
78+
}
79+
80+
return caps;
5681
}
5782

5883
function cleanup(obj) {

tests/unit/utils/reportHandler.js

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
const assert = require('assert');
2+
const sinon = require('sinon');
3+
const { getCapabilities } = require('../../../lib/utilities/reportHandler');
4+
5+
describe('reportHandler.getCapabilities', () => {
6+
const sandbox = sinon.createSandbox();
7+
const originalEnv = process.env.LOCAL_VOLUME_CAPABILITY;
8+
9+
afterEach(() => {
10+
sandbox.restore();
11+
if (originalEnv === undefined) {
12+
delete process.env.LOCAL_VOLUME_CAPABILITY;
13+
} else {
14+
process.env.LOCAL_VOLUME_CAPABILITY = originalEnv;
15+
}
16+
});
17+
18+
describe('getCapabilities', () => {
19+
it('should return default capabilities when config.capabilities is not set', () => {
20+
const cfg = {
21+
supportedLifecycleRules: ['Expiration', 'Transition', 'NoncurrentVersionExpiration'],
22+
};
23+
const caps = getCapabilities(cfg);
24+
25+
assert.deepStrictEqual(caps, {
26+
locationTypeAzure: true,
27+
locationTypeGCP: true,
28+
locationTypeDigitalOcean: true,
29+
locationTypeS3Custom: true,
30+
locationTypeSproxyd: true,
31+
locationTypeNFS: true,
32+
locationTypeCephRadosGW: true,
33+
locationTypeHyperdriveV2: true,
34+
locationTypeLocal: true,
35+
preferredReadLocation: true,
36+
managedLifecycle: true,
37+
managedLifecycleTransition: true,
38+
secureChannelOptimizedPath: true,
39+
s3cIngestLocation: true,
40+
nfsIngestLocation: false,
41+
cephIngestLocation: false,
42+
awsIngestLocation: false,
43+
});
44+
});
45+
46+
it('should use capabilities from config when specified', () => {
47+
const cfg = {
48+
capabilities: {
49+
locationTypeAzure: false,
50+
locationTypeGCP: false,
51+
locationTypeDigitalOcean: true,
52+
locationTypeS3Custom: false,
53+
customCapability: 'test-value',
54+
},
55+
supportedLifecycleRules: ['Expiration'],
56+
};
57+
const caps = getCapabilities(cfg);
58+
59+
assert.deepStrictEqual(caps, {
60+
locationTypeAzure: false,
61+
locationTypeGCP: false,
62+
locationTypeDigitalOcean: true,
63+
locationTypeS3Custom: false,
64+
customCapability: 'test-value',
65+
});
66+
});
67+
68+
it('should apply LOCAL_VOLUME_CAPABILITY env when set to false', () => {
69+
process.env.LOCAL_VOLUME_CAPABILITY = 'false';
70+
const cfg = {
71+
capabilities: {
72+
locationTypeLocal: true,
73+
},
74+
supportedLifecycleRules: ['Expiration'],
75+
};
76+
const caps = getCapabilities(cfg);
77+
78+
// locationTypeLocal should be forced to false due to env variable
79+
assert.strictEqual(caps.locationTypeLocal, false);
80+
});
81+
82+
it('should apply LOCAL_VOLUME_CAPABILITY env when set to "0"', () => {
83+
process.env.LOCAL_VOLUME_CAPABILITY = '0';
84+
const cfg = {
85+
capabilities: {
86+
locationTypeLocal: true,
87+
},
88+
supportedLifecycleRules: ['Expiration'],
89+
};
90+
const caps = getCapabilities(cfg);
91+
92+
// locationTypeLocal should be forced to false due to env variable
93+
assert.strictEqual(caps.locationTypeLocal, false);
94+
});
95+
96+
it('should apply LOCAL_VOLUME_CAPABILITY env when set to true', () => {
97+
process.env.LOCAL_VOLUME_CAPABILITY = '1';
98+
const cfg = {
99+
capabilities: {
100+
locationTypeLocal: true,
101+
},
102+
supportedLifecycleRules: ['Expiration'],
103+
};
104+
const caps = getCapabilities(cfg);
105+
106+
// locationTypeLocal should remain true
107+
assert.strictEqual(caps.locationTypeLocal, true);
108+
});
109+
110+
it('should not apply LOCAL_VOLUME_CAPABILITY env if locationTypeLocal disabled', () => {
111+
process.env.LOCAL_VOLUME_CAPABILITY = true;
112+
const cfg = {
113+
capabilities: {
114+
locationTypeLocal: false,
115+
},
116+
supportedLifecycleRules: ['Expiration'],
117+
};
118+
const caps = getCapabilities(cfg);
119+
120+
// locationTypeLocal should remain true
121+
assert.strictEqual(caps.locationTypeLocal, false);
122+
});
123+
124+
it('should disable managedLifecycle when Expiration is not in supportedLifecycleRules', () => {
125+
const cfg = {
126+
capabilities: {
127+
managedLifecycle: true,
128+
},
129+
supportedLifecycleRules: ['Transition', 'NoncurrentVersionExpiration'],
130+
};
131+
const caps = getCapabilities(cfg);
132+
133+
// managedLifecycle should be false
134+
assert.strictEqual(caps.managedLifecycle, false);
135+
});
136+
137+
it('should keep managedLifecycle when Expiration is in supportedLifecycleRules', () => {
138+
const cfg = {
139+
capabilities: {
140+
managedLifecycle: true,
141+
},
142+
supportedLifecycleRules: ['Expiration', 'Transition'],
143+
};
144+
const caps = getCapabilities(cfg);
145+
146+
// managedLifecycle should remain true
147+
assert.strictEqual(caps.managedLifecycle, true);
148+
});
149+
150+
it('should not enable managedLifecycle if managedLifecycle is disabled', () => {
151+
const cfg = {
152+
capabilities: {
153+
managedLifecycle: false,
154+
},
155+
supportedLifecycleRules: ['Expiration', 'Transition'],
156+
};
157+
const caps = getCapabilities(cfg);
158+
159+
// managedLifecycle should remain false
160+
assert.strictEqual(caps.managedLifecycle, false);
161+
});
162+
163+
it('should disable managedLifecycleTransition when Transition is not in supportedLifecycleRules', () => {
164+
const cfg = {
165+
capabilities: {
166+
managedLifecycleTransition: true,
167+
},
168+
supportedLifecycleRules: ['Expiration', 'NoncurrentVersionExpiration'],
169+
};
170+
const caps = getCapabilities(cfg);
171+
172+
// managedLifecycleTransition should be false
173+
assert.strictEqual(caps.managedLifecycleTransition, false);
174+
});
175+
176+
it('should keep managedLifecycleTransition when Transition is in supportedLifecycleRules', () => {
177+
const cfg = {
178+
capabilities: {
179+
managedLifecycleTransition: true,
180+
},
181+
supportedLifecycleRules: ['Expiration', 'Transition'],
182+
};
183+
const caps = getCapabilities(cfg);
184+
185+
// managedLifecycleTransition should remain true
186+
assert.strictEqual(caps.managedLifecycleTransition, true);
187+
});
188+
189+
it('should not enable managedLifecycleTransition if managedLifecycleTransition is disabled', () => {
190+
const cfg = {
191+
capabilities: {
192+
managedLifecycleTransition: true,
193+
},
194+
supportedLifecycleRules: ['Expiration', 'Transition'],
195+
};
196+
const caps = getCapabilities(cfg);
197+
198+
// managedLifecycleTransition should remain true
199+
assert.strictEqual(caps.managedLifecycleTransition, true);
200+
});
201+
202+
it('should override lifecycleRules from supportedLifecycleRules', () => {
203+
const supportedRules = ['Expiration', 'Transition', 'NoncurrentVersionExpiration'];
204+
const cfg = {
205+
capabilities: {
206+
lifecycleRules: ['Expiration'],
207+
},
208+
supportedLifecycleRules: supportedRules,
209+
};
210+
const caps = getCapabilities(cfg);
211+
212+
// lifecycleRules should be set to supportedLifecycleRules
213+
assert.deepStrictEqual(caps.lifecycleRules, supportedRules);
214+
});
215+
216+
it('should update locationTypes capabilities based on locationTypes map', () => {
217+
const cfg = {
218+
capabilities: {
219+
locationTypeAzure: true,
220+
locationTypeGCP: true,
221+
locationTypeDigitalOcean: true,
222+
locationTypeS3Custom: true,
223+
locationTypeSproxyd: true,
224+
locationTypeNFS: true,
225+
locationTypeCephRadosGW: true,
226+
locationTypeHyperdriveV2: true,
227+
locationTypeLocal: true,
228+
locationTypes: [
229+
'location-gcp-v1',
230+
'location-scality-sproxyd-v1',
231+
'location-ceph-radosgw-s3-v1',
232+
'location-file-v1',
233+
'location-scality-artesca-s3-v1',
234+
],
235+
},
236+
supportedLifecycleRules: ['Expiration'],
237+
};
238+
const caps = getCapabilities(cfg);
239+
240+
// Verify locationTypes override the legacy flags
241+
assert.strictEqual(caps.locationTypeAzure, false);
242+
assert.strictEqual(caps.locationTypeGCP, true);
243+
assert.strictEqual(caps.locationTypeDigitalOcean, false);
244+
assert.strictEqual(caps.locationTypeSproxyd, true);
245+
assert.strictEqual(caps.locationTypeNFS, false);
246+
assert.strictEqual(caps.locationTypeCephRadosGW, true);
247+
assert.strictEqual(caps.locationTypeHyperdriveV2, false);
248+
assert.strictEqual(caps.locationTypeLocal, true);
249+
});
250+
251+
it('should handle multiple consistency checks together', () => {
252+
process.env.LOCAL_VOLUME_CAPABILITY = '0';
253+
const cfg = {
254+
capabilities: {
255+
locationTypeLocal: true,
256+
secureChannelOptimizedPath: true,
257+
managedLifecycle: true,
258+
managedLifecycleTransition: true,
259+
locationTypeAzure: true,
260+
locationTypeGCP: true,
261+
locationTypes: ['location-azure-v1'],
262+
},
263+
supportedLifecycleRules: ['Expiration'], // Missing Transition
264+
};
265+
const caps = getCapabilities(cfg);
266+
267+
// All consistency checks should be applied
268+
assert.strictEqual(caps.locationTypeLocal, false); // env override
269+
assert.strictEqual(caps.managedLifecycle, true); // Expiration present
270+
assert.strictEqual(caps.managedLifecycleTransition, false); // Transition missing
271+
assert.strictEqual(caps.locationTypeAzure, true); // locationTypes override
272+
assert.strictEqual(caps.locationTypeGCP, false); // locationTypes override
273+
});
274+
275+
it('should not modify capabilities when locationTypes is not defined', () => {
276+
const cfg = {
277+
capabilities: {
278+
locationTypeAzure: true,
279+
locationTypeGCP: false,
280+
},
281+
supportedLifecycleRules: ['Expiration'],
282+
};
283+
const caps = getCapabilities(cfg);
284+
285+
// Original values should be preserved
286+
assert.strictEqual(caps.locationTypeAzure, true);
287+
assert.strictEqual(caps.locationTypeGCP, false);
288+
});
289+
});
290+
});

0 commit comments

Comments
 (0)