Skip to content

Commit 94f35a7

Browse files
committed
NC | lifecycle | add expire lifecycle rule
Signed-off-by: nadav mizrahi <[email protected]>
1 parent d908885 commit 94f35a7

File tree

3 files changed

+282
-60
lines changed

3 files changed

+282
-60
lines changed

src/manage_nsfs/nc_lifecycle.js

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ const ManageCLIError = require('./manage_nsfs_cli_errors').ManageCLIError;
1212
const path = require('path');
1313
const { throw_cli_error, get_service_status, NOOBAA_SERVICE_NAME } = require('./manage_nsfs_cli_utils');
1414

15-
// TODO:
16-
// implement
15+
// TODO:
16+
// implement
1717
// 1. notifications
1818
// 2. POSIX scanning and filtering per rule
1919
// 3. GPFS ILM policy and apply for scanning and filtering optimization
2020

2121
/**
2222
* run_lifecycle runs the lifecycle workflow
23-
* @param {import('../sdk/config_fs').ConfigFS} config_fs
23+
* @param {import('../sdk/config_fs').ConfigFS} config_fs
2424
* @returns {Promise<Void>}
2525
*/
2626
async function run_lifecycle(config_fs, disable_service_validation) {
@@ -47,7 +47,7 @@ async function run_lifecycle(config_fs, disable_service_validation) {
4747

4848
/**
4949
* throw_if_noobaa_not_active checks if system.json exists and the noobaa service is active
50-
* @param {import('../sdk/config_fs').ConfigFS} config_fs
50+
* @param {import('../sdk/config_fs').ConfigFS} config_fs
5151
* @param {Object} system_json
5252
*/
5353
async function throw_if_noobaa_not_active(config_fs, system_json) {
@@ -64,14 +64,32 @@ async function throw_if_noobaa_not_active(config_fs, system_json) {
6464
}
6565

6666
/**
67-
* get file time since last modified in days
68-
* @param {nb.NativeFSStats} stat
67+
* @param {Number} mtime
68+
* @returns {Number} days since object was last modified
6969
*/
70-
function _get_file_age_days(stat) {
71-
//TODO how much do we care about rounding errors? (it is by days after all)
72-
return (Date.now() - Number(stat.mtimeNsBigint) / 1e6) / 24 / 60 / 60 / 1000;
70+
function _get_file_age_days(mtime) {
71+
return Math.floor((Date.now() - mtime) / 24 / 60 / 60 / 1000);
7372
}
7473

74+
/**
75+
* get the expiration time in days of an object
76+
* if rule is set with date, then rule is applied for all objects after that date
77+
* return -1 to indicate that the date hasn't arrived, so rule should not be applied
78+
* return 0 in case date has arrived so expiration is true for all elements
79+
* return days in case days was defined and not date
80+
* @param {Object} expiration_rule
81+
* @returns {Number}
82+
*/
83+
function _get_expiration_time(expiration_rule) {
84+
if (expiration_rule.date) {
85+
const expiration_date = new Date(expiration_rule.date).getTime();
86+
if (Date.now() < expiration_date) return -1;
87+
return 0;
88+
}
89+
return expiration_rule.days;
90+
}
91+
92+
7593
/**
7694
* checks if tag query_tag is in the list tag_set
7795
* @param {Object} query_tag
@@ -101,17 +119,28 @@ function _file_contain_tags(object_info, filter_tags) {
101119
}
102120

103121
/**
104-
* @param {*} create_params_parsed
122+
* @param {Object} create_params_parsed
105123
* @param {nb.NativeFSStats} stat
106124
*/
107125
function _get_lifecycle_object_info_for_mpu(create_params_parsed, stat) {
108126
return {
109127
key: create_params_parsed.key,
110-
age: _get_file_age_days(stat),
128+
age: _get_file_age_days(stat.mtime.getTime()),
111129
tags: create_params_parsed.tagging,
112130
};
113131
}
114132

133+
/**
134+
* @param {Object} entry list object entry
135+
*/
136+
function _get_lifecycle_object_info_from_list_object_entry(entry) {
137+
return {
138+
key: entry.key,
139+
age: _get_file_age_days(entry.create_time),
140+
size: entry.size,
141+
tags: entry.tagging,
142+
};
143+
}
115144

116145
/**
117146
* @typedef {{
@@ -170,7 +199,8 @@ async function get_delete_candidates(bucket_json, lifecycle_rule, object_sdk, fs
170199
// let reply_objects = []; // TODO: needed for the notification log file
171200
const candidates = {delete_candidates: []};
172201
if (lifecycle_rule.expiration) {
173-
await get_candidates_by_expiration_rule(lifecycle_rule, bucket_json);
202+
const expiration_candidates = await get_candidates_by_expiration_rule(lifecycle_rule, bucket_json, object_sdk);
203+
candidates.delete_candidates = candidates.delete_candidates.concat(expiration_candidates);
174204
if (lifecycle_rule.expiration.days || lifecycle_rule.expiration.expired_object_delete_marker) {
175205
await get_candidates_by_expiration_delete_marker_rule(lifecycle_rule, bucket_json);
176206
}
@@ -187,9 +217,9 @@ async function get_delete_candidates(bucket_json, lifecycle_rule, object_sdk, fs
187217

188218
/**
189219
* validate_rule_enabled checks if the rule is enabled and should be processed
190-
* @param {*} rule
191-
* @param {Object} bucket
192-
* @param {*} now
220+
* @param {*} rule
221+
* @param {Object} bucket
222+
* @param {*} now
193223
* @returns {boolean}
194224
*/
195225
function validate_rule_enabled(rule, bucket, now) {
@@ -208,40 +238,55 @@ function validate_rule_enabled(rule, bucket, now) {
208238

209239
/**
210240
* get_candidates_by_expiration_rule processes the expiration rule
211-
* @param {*} lifecycle_rule
212-
* @param {Object} bucket_json
241+
* @param {*} lifecycle_rule
242+
* @param {Object} bucket_json
213243
*/
214-
async function get_candidates_by_expiration_rule(lifecycle_rule, bucket_json) {
244+
async function get_candidates_by_expiration_rule(lifecycle_rule, bucket_json, object_sdk) {
215245
const is_gpfs = nb_native().fs.gpfs;
216246
if (is_gpfs) {
217-
await get_candidates_by_expiration_rule_gpfs(lifecycle_rule, bucket_json);
247+
return await get_candidates_by_expiration_rule_gpfs(lifecycle_rule, bucket_json);
218248
} else {
219-
await get_candidates_by_expiration_rule_posix(lifecycle_rule, bucket_json);
249+
return await get_candidates_by_expiration_rule_posix(lifecycle_rule, bucket_json, object_sdk);
220250
}
221251
}
222252

223253
/**
224-
*
225-
* @param {*} lifecycle_rule
226-
* @param {Object} bucket_json
254+
*
255+
* @param {*} lifecycle_rule
256+
* @param {Object} bucket_json
227257
*/
228258
async function get_candidates_by_expiration_rule_gpfs(lifecycle_rule, bucket_json) {
229259
// TODO - implement
230260
}
231261

232262
/**
233-
*
234-
* @param {*} lifecycle_rule
235-
* @param {Object} bucket_json
263+
*
264+
* @param {*} lifecycle_rule
265+
* @param {Object} bucket_json
236266
*/
237-
async function get_candidates_by_expiration_rule_posix(lifecycle_rule, bucket_json) {
238-
// TODO - implement
267+
async function get_candidates_by_expiration_rule_posix(lifecycle_rule, bucket_json, object_sdk) {
268+
const expiration = _get_expiration_time(lifecycle_rule.expiration);
269+
if (expiration < 0) return [];
270+
const filter_func = _build_lifecycle_filter({filter: lifecycle_rule.filter, expiration});
271+
272+
const filtered_objects = [];
273+
// TODO list_objects does not accept a filter and works in batch sizes of 1000. should handle batching
274+
// also should maybe create a helper function or add argument for a filter in list object
275+
const objects_list = await object_sdk.list_objects({bucket: bucket_json.name, prefix: lifecycle_rule.filter?.prefix});
276+
objects_list.objects.forEach(obj => {
277+
const object_info = _get_lifecycle_object_info_from_list_object_entry(obj);
278+
if (filter_func(object_info)) {
279+
filtered_objects.push({key: object_info.key});
280+
}
281+
});
282+
return filtered_objects;
283+
239284
}
240285

241286
/**
242287
* get_candidates_by_expiration_delete_marker_rule processes the expiration delete marker rule
243-
* @param {*} lifecycle_rule
244-
* @param {Object} bucket_json
288+
* @param {*} lifecycle_rule
289+
* @param {Object} bucket_json
245290
*/
246291
async function get_candidates_by_expiration_delete_marker_rule(lifecycle_rule, bucket_json) {
247292
// TODO - implement

src/sdk/namespace_fs.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,24 @@ function filter_fs_xattr(xattr) {
299299
return _.pickBy(xattr, (val, key) => key?.startsWith(XATTR_NOOBAA_INTERNAL_PREFIX));
300300
}
301301

302+
function get_tags_from_xattr(xattr) {
303+
const tag_set = [];
304+
for (const [xattr_key, xattr_value] of Object.entries(xattr)) {
305+
if (xattr_key.includes(XATTR_TAG)) {
306+
tag_set.push({
307+
key: xattr_key.replace(XATTR_TAG, ''),
308+
value: xattr_value,
309+
});
310+
}
311+
}
312+
return tag_set;
313+
}
314+
302315
/**
303316
* get_random_delay returns a random delay number between base + min and max
304-
* @param {number} base
305-
* @param {number} min
306-
* @param {number} max
317+
* @param {number} base
318+
* @param {number} min
319+
* @param {number} max
307320
* @returns {number}
308321
*/
309322
function get_random_delay(base, min, max) {
@@ -2086,7 +2099,7 @@ class NamespaceFS {
20862099
////////////////////
20872100

20882101
async get_object_tagging(params, object_sdk) {
2089-
const tag_set = [];
2102+
let tag_set = [];
20902103
let file_path;
20912104
let file;
20922105
const fs_context = this.prepare_fs_context(object_sdk);
@@ -2101,14 +2114,7 @@ class NamespaceFS {
21012114
file = await nb_native().fs.open(fs_context, file_path);
21022115
const stat = await file.stat(fs_context);
21032116
if (stat.xattr) {
2104-
for (const [xattr_key, xattr_value] of Object.entries(stat.xattr)) {
2105-
if (xattr_key.includes(XATTR_TAG)) {
2106-
tag_set.push({
2107-
key: xattr_key.replace(XATTR_TAG, ''),
2108-
value: xattr_value,
2109-
});
2110-
}
2111-
}
2117+
tag_set = get_tags_from_xattr(stat.xattr);
21122118
}
21132119
} catch (err) {
21142120
dbg.error(`NamespaceFS.get_object_tagging: failed in dir ${file_path} with error: `, err);
@@ -2525,14 +2531,14 @@ class NamespaceFS {
25252531
restore_status: GlacierBackend.get_restore_status(stat.xattr, new Date(), this._get_file_path({key})),
25262532
xattr: to_xattr(stat.xattr),
25272533
tag_count,
2534+
tagging: get_tags_from_xattr(stat.xattr),
25282535

25292536
// temp:
25302537
lock_settings: undefined,
25312538
md5_b64: undefined,
25322539
num_parts: undefined,
25332540
sha256_b64: undefined,
25342541
stats: undefined,
2535-
tagging: undefined,
25362542
object_owner: this._get_object_owner()
25372543
};
25382544
}

0 commit comments

Comments
 (0)