Skip to content

Commit c32e443

Browse files
authored
[6.x] [Task Manager] Ensure putTemplate will only create/update the index template (#28540) (#30163)
* [Task Manager] Ensure putTemplate will only create/update the index template (#28540) * get template version * ensure putTemplate only creates/updates the template * fix tests * new test for throwing error re old template * note comment * clean up test * test clarification * store kibana metadata in scheduled task doc * map `dynamic: false` * logging * add kibana uuid * fix tests * last todo * fetching available task uses apiVersion in the query * scheduledAt * ts fix * ts fix * fix snapshot * await to fail test if snapshot does not match * fix bad merge * remove _.get * fix typeError happening in tests * undo merged 7.0 changes
1 parent abd49fa commit c32e443

File tree

11 files changed

+336
-58
lines changed

11 files changed

+336
-58
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import xPackage from '../../package.json';
8+
import { getTemplateVersion } from './lib/get_template_version';
9+
10+
export const TASK_MANAGER_API_VERSION = 1;
11+
export const TASK_MANAGER_TEMPLATE_VERSION = getTemplateVersion(xPackage.version);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { getTemplateVersion } from './get_template_version';
8+
9+
describe('getTemplateVersion', () => {
10+
it('converts a release build version string into an integer', () => {
11+
const versionStr1 = '7.1.2';
12+
expect(getTemplateVersion(versionStr1)).toBe(7010299);
13+
14+
const versionStr2 = '10.1.0';
15+
expect(getTemplateVersion(versionStr2)).toBe(10010099);
16+
});
17+
18+
it('converts a alpha build version string into an integer', () => {
19+
const versionStr1 = '7.0.0-alpha1';
20+
expect(getTemplateVersion(versionStr1)).toBe(7000001);
21+
22+
const versionStr2 = '7.0.0-alpha3';
23+
expect(getTemplateVersion(versionStr2)).toBe(7000003);
24+
});
25+
26+
it('converts a beta build version string into an integer', () => {
27+
const versionStr1 = '7.0.0-beta4';
28+
expect(getTemplateVersion(versionStr1)).toBe(7000029);
29+
30+
const versionStr5 = '7.0.0-beta8';
31+
expect(getTemplateVersion(versionStr5)).toBe(7000033);
32+
});
33+
34+
it('converts a snapshot build version string into an integer', () => {
35+
expect(getTemplateVersion('8.0.0-alpha1')).toBe(8000001);
36+
expect(getTemplateVersion('8.0.0-alpha1-snapshot')).toBe(8000001);
37+
});
38+
39+
it('not intended to handle any version parts with 3-digits: it will create malformed integer values', () => {
40+
expect(getTemplateVersion('60.61.1') === getTemplateVersion('6.6.101')).toBe(true); // both produce 60610199
41+
expect(getTemplateVersion('1.32.0') < getTemplateVersion('1.3.223423')).toBe(true);
42+
});
43+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { padLeft } from 'lodash';
8+
9+
/*
10+
* The logic for ID is: XXYYZZAA, where XX is major version, YY is minor
11+
* version, ZZ is revision, and AA is alpha/beta/rc indicator.
12+
*
13+
* AA values below 25 are for alpha builder (since 5.0), and above 25 and below
14+
* 50 are beta builds, and below 99 are RC builds, with 99 indicating a release
15+
* the (internal) format of the id is there so we can easily do after/before
16+
* checks on the id
17+
*
18+
* Note: the conversion method is carried over from Elasticsearch:
19+
* https://github.com/elastic/elasticsearch/blob/de962b2/server/src/main/java/org/elasticsearch/Version.java
20+
*/
21+
export function getTemplateVersion(versionStr: string): number {
22+
// break up the string parts
23+
const splitted = versionStr.split('.');
24+
const minorStr = splitted[2] || '';
25+
26+
// pad each part with leading 0 to make 2 characters
27+
const padded = splitted.map((v: string) => {
28+
const vMatches = v.match(/\d+/);
29+
if (vMatches) {
30+
return padLeft(vMatches[0], 2, '0');
31+
}
32+
return '00';
33+
});
34+
const [majorV, minorV, patchV] = padded;
35+
36+
// append the alpha/beta/rc indicator
37+
let buildV;
38+
if (minorStr.match('alpha')) {
39+
const matches = minorStr.match(/alpha(?<alpha>\d+)/);
40+
if (matches != null && matches.groups != null) {
41+
const alphaVerInt = parseInt(matches.groups.alpha, 10); // alpha build indicator
42+
buildV = padLeft(`${alphaVerInt}`, 2, '0');
43+
}
44+
} else if (minorStr.match('beta')) {
45+
const matches = minorStr.match(/beta(?<beta>\d+)/);
46+
if (matches != null && matches.groups != null) {
47+
const alphaVerInt = parseInt(matches.groups.beta, 10) + 25; // beta build indicator
48+
buildV = padLeft(`${alphaVerInt}`, 2, '0');
49+
}
50+
} else {
51+
buildV = '99'; // release build indicator
52+
}
53+
54+
const joinedParts = [majorV, minorV, patchV, buildV].join('');
55+
return parseInt(joinedParts, 10);
56+
}

x-pack/plugins/task_manager/lib/middleware.test.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const getMockConcreteTaskInstance = () => {
2424
attempts: number;
2525
status: TaskStatus;
2626
runAt: Date;
27+
scheduledAt: Date;
2728
state: any;
2829
taskType: string;
2930
params: any;
@@ -33,6 +34,7 @@ const getMockConcreteTaskInstance = () => {
3334
attempts: 0,
3435
status: 'idle',
3536
runAt: new Date(moment('2018-09-18T05:33:09.588Z').valueOf()),
37+
scheduledAt: new Date(moment('2018-09-18T05:33:09.588Z').valueOf()),
3638
state: {},
3739
taskType: 'nice_task',
3840
params: { abc: 'def' },
@@ -53,7 +55,7 @@ const defaultBeforeRun = async (opts: RunContext) => {
5355
};
5456

5557
describe('addMiddlewareToChain', () => {
56-
it('chains the beforeSave functions', () => {
58+
it('chains the beforeSave functions', async () => {
5759
const m1 = {
5860
beforeSave: async (opts: BeforeSaveOpts) => {
5961
Object.assign(opts.taskInstance.params, { m1: true });
@@ -80,8 +82,10 @@ describe('addMiddlewareToChain', () => {
8082
middlewareChain = addMiddlewareToChain(m1, m2);
8183
middlewareChain = addMiddlewareToChain(middlewareChain, m3);
8284

83-
middlewareChain.beforeSave({ taskInstance: getMockTaskInstance() }).then((saveOpts: any) => {
84-
expect(saveOpts).toMatchInlineSnapshot(`
85+
await middlewareChain
86+
.beforeSave({ taskInstance: getMockTaskInstance() })
87+
.then((saveOpts: any) => {
88+
expect(saveOpts).toMatchInlineSnapshot(`
8589
Object {
8690
"taskInstance": Object {
8791
"params": Object {
@@ -95,10 +99,10 @@ Object {
9599
},
96100
}
97101
`);
98-
});
102+
});
99103
});
100104

101-
it('chains the beforeRun functions', () => {
105+
it('chains the beforeRun functions', async () => {
102106
const m1 = {
103107
beforeSave: defaultBeforeSave,
104108
beforeRun: async (opts: RunContext) => {
@@ -131,7 +135,7 @@ Object {
131135
middlewareChain = addMiddlewareToChain(m1, m2);
132136
middlewareChain = addMiddlewareToChain(middlewareChain, m3);
133137

134-
middlewareChain
138+
await middlewareChain
135139
.beforeRun(getMockRunContext(getMockConcreteTaskInstance()))
136140
.then(contextOpts => {
137141
expect(contextOpts).toMatchInlineSnapshot(`
@@ -147,6 +151,7 @@ Object {
147151
"abc": "def",
148152
},
149153
"runAt": 2018-09-18T05:33:09.588Z,
154+
"scheduledAt": 2018-09-18T05:33:09.588Z,
150155
"state": Object {},
151156
"status": "idle",
152157
"taskType": "nice_task",

x-pack/plugins/task_manager/task.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ export interface TaskInstance {
158158
*/
159159
taskType: string;
160160

161+
/**
162+
* The date and time that this task was originally scheduled. This is used
163+
* for convenience to task run functions, and for troubleshooting.
164+
*/
165+
scheduledAt?: Date;
166+
161167
/**
162168
* The date and time that this task is scheduled to be run. It is not
163169
* guaranteed to run at this time, but it is guaranteed not to run earlier
@@ -210,6 +216,12 @@ export interface ConcreteTaskInstance extends TaskInstance {
210216
*/
211217
version: number;
212218

219+
/**
220+
* The date and time that this task was originally scheduled. This is used
221+
* for convenience to task run functions, and for troubleshooting.
222+
*/
223+
scheduledAt: Date;
224+
213225
/**
214226
* The number of unsuccessful attempts since the last successful run. This
215227
* will be zeroed out after a successful run.

x-pack/plugins/task_manager/task_manager.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ describe('TaskManager', () => {
7575
});
7676

7777
function testOpts() {
78+
const callCluster = sinon.stub();
79+
callCluster.withArgs('indices.getTemplate').returns(Promise.resolve({ tasky: {} }));
80+
7881
const $test = {
7982
events: {} as any,
8083
afterPluginsInit: _.noop,
@@ -100,7 +103,7 @@ describe('TaskManager', () => {
100103
plugins: {
101104
elasticsearch: {
102105
getCluster() {
103-
return { callWithInternalUser: _.noop };
106+
return { callWithInternalUser: callCluster };
104107
},
105108
status: {
106109
on(eventName: string, callback: () => any) {

x-pack/plugins/task_manager/task_manager.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,15 @@ export class TaskManager {
5353

5454
const logger = new TaskManagerLogger((...args: any[]) => server.log(...args));
5555

56+
/* Kibana UUID needs to be pulled live (not cached), as it takes a long time
57+
* to initialize, and can change after startup */
5658
const store = new TaskStore({
5759
callCluster: server.plugins.elasticsearch.getCluster('admin').callWithInternalUser,
5860
index: config.get('xpack.task_manager.index'),
5961
maxAttempts: config.get('xpack.task_manager.max_attempts'),
6062
supportedTypes: Object.keys(this.definitions),
63+
logger,
64+
getKibanaUuid: () => config.get('server.uuid'),
6165
});
6266
const pool = new TaskPool({
6367
logger,
@@ -94,6 +98,7 @@ export class TaskManager {
9498
this.isInitialized = true;
9599
})
96100
.catch((err: Error) => {
101+
// FIXME: check the type of error to make sure it's actually an ES error
97102
logger.warning(err.message);
98103

99104
// rety again to initialize store and poller, using the timing of

x-pack/plugins/task_manager/task_poller.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@ let store: TaskStore;
1414

1515
describe('TaskPoller', () => {
1616
beforeEach(() => {
17-
const callCluster = sinon.spy();
17+
const callCluster = sinon.stub();
18+
callCluster.withArgs('indices.getTemplate').returns(Promise.resolve({ tasky: {} }));
19+
const getKibanaUuid = sinon.stub().returns('kibana-123-uuid-test');
1820
store = new TaskStore({
1921
callCluster,
22+
getKibanaUuid,
23+
logger: mockLogger(),
2024
index: 'tasky',
2125
maxAttempts: 2,
2226
supportedTypes: ['a', 'b', 'c'],

x-pack/plugins/task_manager/task_runner.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ describe('TaskManagerRunner', () => {
232232
taskType: 'bar',
233233
version: 32,
234234
runAt: new Date(),
235+
scheduledAt: new Date(),
235236
attempts: 0,
236237
params: {},
237238
scope: ['reporting'],

0 commit comments

Comments
 (0)