Compare commits
2 Commits
1ae4abee19
...
2d60f5c480
Author | SHA1 | Date |
---|---|---|
Taylor McKinnon | 2d60f5c480 | |
Taylor McKinnon | f649b3d90a |
|
@ -17,6 +17,6 @@ if [ -z "$SETUP_CMD" ]; then
|
||||||
SETUP_CMD="start"
|
SETUP_CMD="start"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
UTAPI_INTERVAL_TEST_MODE=$1 npm $SETUP_CMD 2>&1 | tee -a "/artifacts/setup_$2.log" &
|
UTAPI_FILTER_BUCKET_DENY='deny-this-bucket' UTAPI_INTERVAL_TEST_MODE=$1 npm $SETUP_CMD 2>&1 | tee -a "/artifacts/setup_$2.log" &
|
||||||
bash tests/utils/wait_for_local_port.bash $PORT 40
|
bash tests/utils/wait_for_local_port.bash $PORT 40
|
||||||
UTAPI_INTERVAL_TEST_MODE=$1 npm run $2 | tee -a "/artifacts/test_$2.log"
|
UTAPI_INTERVAL_TEST_MODE=$1 npm run $2 | tee -a "/artifacts/test_$2.log"
|
||||||
|
|
|
@ -48,5 +48,9 @@
|
||||||
"serviceUser": {
|
"serviceUser": {
|
||||||
"arn": "arn:aws:iam::000000000000:user/service-utapi-user",
|
"arn": "arn:aws:iam::000000000000:user/service-utapi-user",
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
},
|
||||||
|
"filter": {
|
||||||
|
"allow": {},
|
||||||
|
"deny": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ const path = require('path');
|
||||||
const Joi = require('@hapi/joi');
|
const Joi = require('@hapi/joi');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
const { truthy, envNamespace } = require('../constants');
|
const {
|
||||||
|
truthy, envNamespace, allowedFilterFields, allowedFilterStates,
|
||||||
|
} = require('../constants');
|
||||||
const configSchema = require('./schema');
|
const configSchema = require('./schema');
|
||||||
// We need to require the specific file rather than the parent module to avoid a circular require
|
// We need to require the specific file rather than the parent module to avoid a circular require
|
||||||
const { parseDiskSizeSpec } = require('../utils/disk');
|
const { parseDiskSizeSpec } = require('../utils/disk');
|
||||||
|
@ -225,6 +227,28 @@ class Config {
|
||||||
return certs;
|
return certs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static _parseResourceFilters(config) {
|
||||||
|
const resourceFilters = {};
|
||||||
|
|
||||||
|
allowedFilterFields.forEach(
|
||||||
|
field => allowedFilterStates.forEach(
|
||||||
|
state => {
|
||||||
|
const configResources = (config[state] && config[state][field]) || null;
|
||||||
|
const envVar = `FILTER_${field.toUpperCase()}_${state.toUpperCase()}`;
|
||||||
|
const resources = _loadFromEnv(envVar, configResources, _typeCasts.list);
|
||||||
|
if (resources) {
|
||||||
|
if (resourceFilters[field]) {
|
||||||
|
throw new Error('You can not define both an allow and a deny list for an event field.');
|
||||||
|
}
|
||||||
|
resourceFilters[field] = { [state]: new Set(resources) };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return resourceFilters;
|
||||||
|
}
|
||||||
|
|
||||||
_parseConfig(config) {
|
_parseConfig(config) {
|
||||||
const parsedConfig = {};
|
const parsedConfig = {};
|
||||||
|
|
||||||
|
@ -345,6 +369,8 @@ class Config {
|
||||||
enabled: _loadFromEnv('SERVICE_USER_ENABLED', config.serviceUser.enabled, _typeCasts.bool),
|
enabled: _loadFromEnv('SERVICE_USER_ENABLED', config.serviceUser.enabled, _typeCasts.bool),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
parsedConfig.filter = Config._parseResourceFilters(config.filter);
|
||||||
|
|
||||||
return parsedConfig;
|
return parsedConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const Joi = require('@hapi/joi');
|
const Joi = require('@hapi/joi');
|
||||||
|
const { allowedFilterFields, allowedFilterStates } = require('../constants');
|
||||||
|
|
||||||
const redisServerSchema = Joi.object({
|
const redisServerSchema = Joi.object({
|
||||||
host: Joi.string(),
|
host: Joi.string(),
|
||||||
|
@ -89,6 +90,17 @@ const schema = Joi.object({
|
||||||
arn: Joi.string(),
|
arn: Joi.string(),
|
||||||
enabled: Joi.boolean(),
|
enabled: Joi.boolean(),
|
||||||
}),
|
}),
|
||||||
|
filter: Joi.object(allowedFilterStates.reduce(
|
||||||
|
(filterObj, state) => {
|
||||||
|
filterObj[state] = allowedFilterFields.reduce(
|
||||||
|
(stateObj, field) => {
|
||||||
|
stateObj[field] = Joi.string();
|
||||||
|
return stateObj;
|
||||||
|
}, {},
|
||||||
|
);
|
||||||
|
return filterObj;
|
||||||
|
}, {},
|
||||||
|
)),
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = schema;
|
module.exports = schema;
|
||||||
|
|
|
@ -111,6 +111,15 @@ const constants = {
|
||||||
putDeleteMarkerObject: 'deleteObject',
|
putDeleteMarkerObject: 'deleteObject',
|
||||||
},
|
},
|
||||||
expirationChunkDuration: 900000000, // 15 minutes in microseconds
|
expirationChunkDuration: 900000000, // 15 minutes in microseconds
|
||||||
|
allowedFilterFields: [
|
||||||
|
'operationId',
|
||||||
|
'location',
|
||||||
|
'account',
|
||||||
|
'user',
|
||||||
|
'bucket',
|
||||||
|
'object',
|
||||||
|
],
|
||||||
|
allowedFilterStates: ['allow', 'deny'],
|
||||||
};
|
};
|
||||||
|
|
||||||
constants.operationToResponse = constants.operations
|
constants.operationToResponse = constants.operations
|
||||||
|
|
|
@ -5,7 +5,7 @@ const { UtapiMetric } = require('../models');
|
||||||
const config = require('../config');
|
const config = require('../config');
|
||||||
const { checkpointLagSecs } = require('../constants');
|
const { checkpointLagSecs } = require('../constants');
|
||||||
const {
|
const {
|
||||||
LoggerContext, shardFromTimestamp, convertTimestamp, InterpolatedClock, now,
|
LoggerContext, shardFromTimestamp, convertTimestamp, InterpolatedClock, now, filterObject,
|
||||||
} = require('../utils');
|
} = require('../utils');
|
||||||
|
|
||||||
const logger = new LoggerContext({
|
const logger = new LoggerContext({
|
||||||
|
@ -20,6 +20,11 @@ class IngestShardTask extends BaseTask {
|
||||||
this._defaultSchedule = config.ingestionSchedule;
|
this._defaultSchedule = config.ingestionSchedule;
|
||||||
this._defaultLag = config.ingestionLagSeconds;
|
this._defaultLag = config.ingestionLagSeconds;
|
||||||
this._stripEventUUID = options.stripEventUUID !== undefined ? options.stripEventUUID : true;
|
this._stripEventUUID = options.stripEventUUID !== undefined ? options.stripEventUUID : true;
|
||||||
|
this._eventFilter = Object.entries(config.filter)
|
||||||
|
.reduce((chain, [level, filter]) => {
|
||||||
|
const filterFunc = filterObject(level, filter);
|
||||||
|
return obj => filterFunc(obj) && chain(obj);
|
||||||
|
}, () => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_hydrateEvent(data, stripTimestamp = false) {
|
_hydrateEvent(data, stripTimestamp = false) {
|
||||||
|
@ -61,7 +66,9 @@ class IngestShardTask extends BaseTask {
|
||||||
logger.info('Detected slow records, ingesting as repair');
|
logger.info('Detected slow records, ingesting as repair');
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = metrics.map(m => this._hydrateEvent(m, areSlowEvents));
|
const records = metrics
|
||||||
|
.map(m => this._hydrateEvent(m, areSlowEvents))
|
||||||
|
.filter(m => this._eventFilter(m));
|
||||||
|
|
||||||
records.sort((a, b) => a.timestamp - b.timestamp);
|
records.sort((a, b) => a.timestamp - b.timestamp);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
function filterObject(key, { allow, deny }) {
|
||||||
|
if (allow && deny) {
|
||||||
|
throw new Error('You can not define both an allow and a deny list.');
|
||||||
|
}
|
||||||
|
if (!allow && !deny) {
|
||||||
|
throw new Error('You must define either an allow or a deny list.');
|
||||||
|
}
|
||||||
|
if (allow) {
|
||||||
|
return obj => (obj[key] === undefined) || allow.has(obj[key]);
|
||||||
|
}
|
||||||
|
return obj => (obj[key] === undefined) || !deny.has(obj[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { filterObject };
|
|
@ -3,6 +3,7 @@ const shard = require('./shard');
|
||||||
const timestamp = require('./timestamp');
|
const timestamp = require('./timestamp');
|
||||||
const func = require('./func');
|
const func = require('./func');
|
||||||
const disk = require('./disk');
|
const disk = require('./disk');
|
||||||
|
const filter = require('./filter');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...log,
|
...log,
|
||||||
|
@ -10,4 +11,5 @@ module.exports = {
|
||||||
...timestamp,
|
...timestamp,
|
||||||
...func,
|
...func,
|
||||||
...disk,
|
...disk,
|
||||||
|
...filter,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
const assert = require('assert');
|
||||||
|
const { filterObject } = require('../../../../libV2/utils');
|
||||||
|
|
||||||
|
const testCases = [
|
||||||
|
{
|
||||||
|
filter: { allow: new Set(['foo', 'bar']) },
|
||||||
|
value: 'foo',
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: { allow: new Set(['foo', 'bar']) },
|
||||||
|
value: 'baz',
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: { deny: new Set(['foo', 'bar']) },
|
||||||
|
value: 'foo',
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: { deny: new Set(['foo', 'bar']) },
|
||||||
|
value: 'baz',
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe'Test filterObject', () => {
|
||||||
|
testCases.forEach(testCase => {
|
||||||
|
const { value, expected, filter } = testCase;
|
||||||
|
const successMsg = expected ? 'should not filter' : 'should filter';
|
||||||
|
const state = (expected && filter.allow) || (!expected && filter.deny) ? '' : ' not';
|
||||||
|
const ruleType = Object.keys(filter)[0];
|
||||||
|
const msg = `${successMsg} object if value is${state} present in ${ruleType} list`;
|
||||||
|
it(msg, () => {
|
||||||
|
const func = filterObject('value', filter);
|
||||||
|
assert.strictEqual(func({ value }), expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not filter an object if the filter key in undefined', () => {
|
||||||
|
const func = filterObject('value', { allow: ['foo'] });
|
||||||
|
assert.strictEqual(func({}), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if creating a filter with both allow and deny lists', () => {
|
||||||
|
assert.throws(() => filterObject('value', { allow: ['foo'], deny: ['bar'] }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if creating a filter without an allow or deny lists', () => {
|
||||||
|
assert.throws(() => filterObject('value', {}));
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue