Skip to content

Commit

Permalink
Make policy change action BWC with agent <= 7.9
Browse files Browse the repository at this point in the history
  • Loading branch information
jen-huang committed Sep 22, 2020
1 parent 4a4e634 commit 9c849fa
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 19 deletions.
17 changes: 14 additions & 3 deletions x-pack/plugins/ingest_manager/common/types/models/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { FullAgentPolicy } from './agent_policy';
import { AGENT_TYPE_EPHEMERAL, AGENT_TYPE_PERMANENT, AGENT_TYPE_TEMPORARY } from '../../constants';

export type AgentType =
Expand All @@ -21,7 +21,7 @@ export type AgentStatus =
| 'unenrolling'
| 'degraded';

export type AgentActionType = 'POLICY_CHANGE' | 'CONFIG_CHANGE' | 'UNENROLL';
export type AgentActionType = 'POLICY_CHANGE' | 'UNENROLL';

export interface NewAgentAction {
type: AgentActionType;
Expand All @@ -42,13 +42,24 @@ export interface AgentAction extends NewAgentAction {
export interface AgentPolicyAction extends NewAgentAction {
id: string;
type: AgentActionType;
data?: any;
data: {
policy: FullAgentPolicy;
};
policy_id: string;
policy_revision: number;
created_at: string;
ack_data?: any;
}

// Make policy change action renaming BWC with agent version <= 7.9
// eslint-disable-next-line @typescript-eslint/naming-convention
export type AgentPolicyActionV7_9 = Omit<AgentPolicyAction, 'type' | 'data'> & {
type: 'CONFIG_CHANGE';
data: {
config: FullAgentPolicy;
};
};

interface CommonAgentActionSOAttributes {
type: AgentActionType;
sent_at?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,8 @@ class AgentPolicyService {
}, []);

await createAgentPolicyAction(soClient, {
// This is problematic...
type: 'POLICY_CHANGE',
data: { config: policy, policy } as any,
data: { policy },
ack_data: { packages },
created_at: new Date().toISOString(),
policy_id: policy.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ describe('test agent acks services', () => {
const mockSavedObjectsClient = savedObjectsClientMock.create();

const actionAttributes = {
type: 'CONFIG_CHANGE',
type: 'POLICY_CHANGE',
policy_id: 'policy1',
policy_revision: 4,
sent_at: '2020-03-14T19:45:02.620Z',
Expand Down Expand Up @@ -180,7 +180,7 @@ describe('test agent acks services', () => {
const mockSavedObjectsClient = savedObjectsClientMock.create();

const actionAttributes = {
type: 'CONFIG_CHANGE',
type: 'POLICY_CHANGE',
policy_id: 'policy1',
policy_revision: 4,
sent_at: '2020-03-14T19:45:02.620Z',
Expand Down
11 changes: 7 additions & 4 deletions x-pack/plugins/ingest_manager/server/services/agents/acks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
Agent,
AgentAction,
AgentPolicyAction,
AgentPolicyActionV7_9,
AgentEvent,
AgentEventSOAttributes,
AgentSOAttributes,
Expand Down Expand Up @@ -126,15 +127,17 @@ async function fetchActionsUsingCache(
return [...freshActions, ...actions];
}

function isAgentPolicyAction(action: AgentAction | AgentPolicyAction): action is AgentPolicyAction {
function isAgentPolicyAction(
action: AgentAction | AgentPolicyAction | AgentPolicyActionV7_9
): action is AgentPolicyAction | AgentPolicyActionV7_9 {
return (action as AgentPolicyAction).policy_id !== undefined;
}

function getLatestConfigChangePolicyActionIfUpdated(
agent: Agent,
actions: Array<AgentAction | AgentPolicyAction>
): AgentPolicyAction | null {
return actions.reduce<null | AgentPolicyAction>((acc, action) => {
actions: Array<AgentAction | AgentPolicyAction | AgentPolicyActionV7_9>
): AgentPolicyAction | AgentPolicyActionV7_9 | null {
return actions.reduce<null | AgentPolicyAction | AgentPolicyActionV7_9>((acc, action) => {
if (
!isAgentPolicyAction(action) ||
(action.type !== 'POLICY_CHANGE' && action.type !== 'CONFIG_CHANGE') ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { savedObjectsClientMock } from 'src/core/server/mocks';
import { createAgentActionFromPolicyAction } from './state_new_actions';
import { OutputType, Agent, AgentPolicyAction } from '../../../types';

jest.mock('../../app_context', () => ({
appContextService: {
getEncryptedSavedObjects: () => ({
getDecryptedAsInternalUser: () => ({
attributes: {
default_api_key: 'MOCK_API_KEY',
},
}),
}),
},
}));

describe('test agent checkin new action services', () => {
describe('createAgentActionFromPolicyAction()', () => {
const mockSavedObjectsClient = savedObjectsClientMock.create();
const mockAgent: Agent = {
id: 'agent1',
active: true,
type: 'PERMANENT',
local_metadata: { elastic: { agent: { version: '7.10.0' } } },
user_provided_metadata: {},
current_error_events: [],
packages: [],
enrolled_at: '2020-03-14T19:45:02.620Z',
};
const mockPolicyAction: AgentPolicyAction = {
id: 'action1',
type: 'POLICY_CHANGE',
policy_id: 'policy1',
policy_revision: 1,
sent_at: '2020-03-14T19:45:02.620Z',
created_at: '2020-03-14T19:45:02.620Z',
data: {
policy: {
id: 'policy1',
outputs: {
default: {
type: OutputType.Elasticsearch,
hosts: [],
ca_sha256: undefined,
api_key: undefined,
},
},
inputs: [],
},
},
};

it('should return POLICY_CHANGE and data.policy for agent version >= 7.10', async () => {
const expectedResult = [
{
agent_id: 'agent1',
created_at: '2020-03-14T19:45:02.620Z',
data: {
policy: {
id: 'policy1',
inputs: [],
outputs: { default: { api_key: 'MOCK_API_KEY', hosts: [], type: 'elasticsearch' } },
},
},
id: 'action1',
sent_at: '2020-03-14T19:45:02.620Z',
type: 'POLICY_CHANGE',
},
];

expect(
await createAgentActionFromPolicyAction(mockSavedObjectsClient, mockAgent, mockPolicyAction)
).toEqual(expectedResult);

expect(
await createAgentActionFromPolicyAction(
mockSavedObjectsClient,
{ ...mockAgent, local_metadata: { elastic: { agent: { version: '7.10.1-SNAPSHOT' } } } },
mockPolicyAction
)
).toEqual(expectedResult);

expect(
await createAgentActionFromPolicyAction(
mockSavedObjectsClient,
{ ...mockAgent, local_metadata: { elastic: { agent: { version: '8.0.0' } } } },
mockPolicyAction
)
).toEqual(expectedResult);

expect(
await createAgentActionFromPolicyAction(
mockSavedObjectsClient,
{ ...mockAgent, local_metadata: { elastic: { agent: { version: '8.0.0-SNAPSHOT' } } } },
mockPolicyAction
)
).toEqual(expectedResult);
});

it('should return CONNFIG_CHANGE and data.config for agent version <= 7.9', async () => {
const expectedResult = [
{
agent_id: 'agent1',
created_at: '2020-03-14T19:45:02.620Z',
data: {
config: {
id: 'policy1',
inputs: [],
outputs: { default: { api_key: 'MOCK_API_KEY', hosts: [], type: 'elasticsearch' } },
},
},
id: 'action1',
sent_at: '2020-03-14T19:45:02.620Z',
type: 'CONFIG_CHANGE',
},
];

expect(
await createAgentActionFromPolicyAction(
mockSavedObjectsClient,
{ ...mockAgent, local_metadata: { elastic: { agent: { version: '7.9.0' } } } },
mockPolicyAction
)
).toEqual(expectedResult);

expect(
await createAgentActionFromPolicyAction(
mockSavedObjectsClient,
{ ...mockAgent, local_metadata: { elastic: { agent: { version: '7.9.1-SNAPSHOT' } } } },
mockPolicyAction
)
).toEqual(expectedResult);

expect(
await createAgentActionFromPolicyAction(
mockSavedObjectsClient,
{ ...mockAgent, local_metadata: { elastic: { agent: { version: '7.9.3' } } } },
mockPolicyAction
)
).toEqual(expectedResult);

expect(
await createAgentActionFromPolicyAction(
mockSavedObjectsClient,
{ ...mockAgent, local_metadata: { elastic: { agent: { version: '7.8.2' } } } },
mockPolicyAction
)
).toEqual(expectedResult);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import semver from 'semver';
import { timer, from, Observable, TimeoutError } from 'rxjs';
import { omit } from 'lodash';
import {
Expand All @@ -17,7 +17,13 @@ import {
take,
} from 'rxjs/operators';
import { SavedObjectsClientContract, KibanaRequest } from 'src/core/server';
import { Agent, AgentAction, AgentPolicyAction, AgentSOAttributes } from '../../../types';
import {
Agent,
AgentAction,
AgentPolicyAction,
AgentPolicyActionV7_9,
AgentSOAttributes,
} from '../../../types';
import * as APIKeysService from '../../api_keys';
import {
AGENT_SAVED_OBJECT_TYPE,
Expand Down Expand Up @@ -104,15 +110,29 @@ async function getOrCreateAgentDefaultOutputAPIKey(
return outputAPIKey.key;
}

async function createAgentActionFromPolicyAction(
export async function createAgentActionFromPolicyAction(
soClient: SavedObjectsClientContract,
agent: Agent,
policyAction: AgentPolicyAction
) {
// Transform the policy action for agent version <= 7.9 for BWC
const agentVersion = semver.parse((agent.local_metadata?.elastic as any)?.agent?.version);
const agentPolicyAction: AgentPolicyAction | AgentPolicyActionV7_9 =
agentVersion && semver.lt(agentVersion, '7.10.0')
? {
...policyAction,
type: 'CONFIG_CHANGE',
data: {
config: policyAction.data.policy,
},
}
: policyAction;

// Create agent action
const newAgentAction: AgentAction = Object.assign(
omit(
// Faster than clone
JSON.parse(JSON.stringify(policyAction)) as AgentPolicyAction,
JSON.parse(JSON.stringify(agentPolicyAction)) as AgentPolicyAction,
'policy_id',
'policy_revision'
),
Expand All @@ -122,10 +142,14 @@ async function createAgentActionFromPolicyAction(
);

// Mutate the policy to set the api token for this agent
newAgentAction.data.config.outputs.default.api_key = await getOrCreateAgentDefaultOutputAPIKey(
soClient,
agent
);
const apiKey = await getOrCreateAgentDefaultOutputAPIKey(soClient, agent);
if (newAgentAction.data.policy) {
newAgentAction.data.policy.outputs.default.api_key = apiKey;
}
// BWC for agent <= 7.9
else if (newAgentAction.data.config) {
newAgentAction.data.config.outputs.default.api_key = apiKey;
}

return [newAgentAction];
}
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/ingest_manager/server/types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export {
AgentEventSOAttributes,
AgentAction,
AgentPolicyAction,
AgentPolicyActionV7_9,
BaseAgentActionSOAttributes,
AgentActionSOAttributes,
AgentPolicyActionSOAttributes,
Expand Down

0 comments on commit 9c849fa

Please sign in to comment.