Skip to content

Commit

Permalink
[IngestManager] Allow to filter agent by packages
Browse files Browse the repository at this point in the history
  • Loading branch information
nchaulet committed Jun 23, 2020
1 parent 6cc8ea1 commit a71f7b4
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 7 deletions.
2 changes: 2 additions & 0 deletions x-pack/plugins/ingest_manager/common/types/models/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ export interface Agent extends AgentBase {
current_error_events: AgentEvent[];
access_api_key?: string;
status?: string;
packages: string[];
}

export interface AgentSOAttributes extends AgentBase {
current_error_events?: string;
packages?: string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = {
default_api_key: { type: 'keyword' },
updated_at: { type: 'date' },
current_error_events: { type: 'text' },
packages: { type: 'keyword' },
},
},
},
Expand Down
269 changes: 269 additions & 0 deletions x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,275 @@ describe('test agent acks services', () => {
]);
});

it('should update config field on the agent if a config change is acknowledged', async () => {
const mockSavedObjectsClient = savedObjectsClientMock.create();
const mockStartEncryptedSOPlugin = encryptedSavedObjectsMock.createStart();
appContextService.start(({
encryptedSavedObjectsStart: mockStartEncryptedSOPlugin,
} as unknown) as IngestManagerAppContext);

const [
{ value: mockStartEncryptedSOClient },
] = mockStartEncryptedSOPlugin.getClient.mock.results;

mockStartEncryptedSOClient.getDecryptedAsInternalUser.mockReturnValue(
Promise.resolve({
id: 'action1',
references: [],
type: AGENT_ACTION_SAVED_OBJECT_TYPE,
attributes: {
type: 'CONFIG_CHANGE',
agent_id: 'id',
sent_at: '2020-03-14T19:45:02.620Z',
timestamp: '2019-01-04T14:32:03.36764-05:00',
created_at: '2020-03-14T19:45:02.620Z',
data: JSON.stringify({
config: {
id: 'config1',
revision: 4,
settings: {
monitoring: {
enabled: true,
use_output: 'default',
logs: true,
metrics: true,
},
},
outputs: {
default: {
type: 'elasticsearch',
hosts: ['http://localhost:9200'],
},
},
inputs: [
{
id: 'f2293360-b57c-11ea-8bd3-7bd51e425399',
name: 'system-1',
type: 'logs',
use_output: 'default',
package: {
name: 'system',
version: '0.3.0',
},
dataset: {
namespace: 'default',
},
streams: [
{
id: 'logs-system.syslog',
dataset: {
name: 'system.syslog',
},
paths: ['/var/log/messages*', '/var/log/syslog*'],
exclude_files: ['.gz$'],
multiline: {
pattern: '^\\s',
match: 'after',
},
processors: [
{
add_locale: null,
},
{
add_fields: {
target: '',
fields: {
'ecs.version': '1.5.0',
},
},
},
],
},
],
},
],
},
}),
},
})
);

mockSavedObjectsClient.bulkGet.mockReturnValue(
Promise.resolve({
saved_objects: [
{
id: 'action1',
references: [],
type: AGENT_ACTION_SAVED_OBJECT_TYPE,
attributes: {
type: 'CONFIG_CHANGE',
agent_id: 'id',
sent_at: '2020-03-14T19:45:02.620Z',
timestamp: '2019-01-04T14:32:03.36764-05:00',
created_at: '2020-03-14T19:45:02.620Z',
},
},
],
} as SavedObjectsBulkResponse<AgentActionSOAttributes>)
);

await acknowledgeAgentActions(
mockSavedObjectsClient,
({
id: 'id',
type: AGENT_TYPE_PERMANENT,
config_id: 'config1',
} as unknown) as Agent,
[
{
type: 'ACTION_RESULT',
subtype: 'CONFIG',
timestamp: '2019-01-04T14:32:03.36764-05:00',
action_id: 'action1',
agent_id: 'id',
} as AgentEvent,
]
);
expect(mockSavedObjectsClient.bulkUpdate).toBeCalled();
expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toHaveLength(2);
expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0][0]).toMatchInlineSnapshot(`
Object {
"attributes": Object {
"config_revision": 4,
"packages": Array [
"system",
],
},
"id": "id",
"type": "fleet-agents",
}
`);
});

it('should not update config field on the agent if a config change for an old revision is acknowledged', async () => {
const mockSavedObjectsClient = savedObjectsClientMock.create();
const mockStartEncryptedSOPlugin = encryptedSavedObjectsMock.createStart();
appContextService.start(({
encryptedSavedObjectsStart: mockStartEncryptedSOPlugin,
} as unknown) as IngestManagerAppContext);

const [
{ value: mockStartEncryptedSOClient },
] = mockStartEncryptedSOPlugin.getClient.mock.results;

mockStartEncryptedSOClient.getDecryptedAsInternalUser.mockReturnValue(
Promise.resolve({
id: 'action1',
references: [],
type: AGENT_ACTION_SAVED_OBJECT_TYPE,
attributes: {
type: 'CONFIG_CHANGE',
agent_id: 'id',
sent_at: '2020-03-14T19:45:02.620Z',
timestamp: '2019-01-04T14:32:03.36764-05:00',
created_at: '2020-03-14T19:45:02.620Z',
data: JSON.stringify({
config: {
id: 'config1',
revision: 4,
settings: {
monitoring: {
enabled: true,
use_output: 'default',
logs: true,
metrics: true,
},
},
outputs: {
default: {
type: 'elasticsearch',
hosts: ['http://localhost:9200'],
},
},
inputs: [
{
id: 'f2293360-b57c-11ea-8bd3-7bd51e425399',
name: 'system-1',
type: 'logs',
use_output: 'default',
package: {
name: 'system',
version: '0.3.0',
},
dataset: {
namespace: 'default',
},
streams: [
{
id: 'logs-system.syslog',
dataset: {
name: 'system.syslog',
},
paths: ['/var/log/messages*', '/var/log/syslog*'],
exclude_files: ['.gz$'],
multiline: {
pattern: '^\\s',
match: 'after',
},
processors: [
{
add_locale: null,
},
{
add_fields: {
target: '',
fields: {
'ecs.version': '1.5.0',
},
},
},
],
},
],
},
],
},
}),
},
})
);

mockSavedObjectsClient.bulkGet.mockReturnValue(
Promise.resolve({
saved_objects: [
{
id: 'action1',
references: [],
type: AGENT_ACTION_SAVED_OBJECT_TYPE,
attributes: {
type: 'CONFIG_CHANGE',
agent_id: 'id',
sent_at: '2020-03-14T19:45:02.620Z',
timestamp: '2019-01-04T14:32:03.36764-05:00',
created_at: '2020-03-14T19:45:02.620Z',
},
},
],
} as SavedObjectsBulkResponse<AgentActionSOAttributes>)
);

await acknowledgeAgentActions(
mockSavedObjectsClient,
({
id: 'id',
type: AGENT_TYPE_PERMANENT,
config_id: 'config1',
config_revision: 100,
} as unknown) as Agent,
[
{
type: 'ACTION_RESULT',
subtype: 'CONFIG',
timestamp: '2019-01-04T14:32:03.36764-05:00',
action_id: 'action1',
agent_id: 'id',
} as AgentEvent,
]
);
expect(mockSavedObjectsClient.bulkUpdate).toBeCalled();
expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toHaveLength(1);
});

it('should fail for actions that cannot be found on agent actions list', async () => {
const mockSavedObjectsClient = savedObjectsClientMock.create();
mockSavedObjectsClient.bulkGet.mockReturnValue(
Expand Down
27 changes: 20 additions & 7 deletions x-pack/plugins/ingest_manager/server/services/agents/acks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
AgentEventSOAttributes,
AgentSOAttributes,
AgentActionSOAttributes,
FullAgentConfig,
} from '../../types';
import {
AGENT_EVENT_SAVED_OBJECT_TYPE,
Expand Down Expand Up @@ -62,18 +63,18 @@ export async function acknowledgeAgentActions(
if (actions.length === 0) {
return [];
}
const configRevision = getLatestConfigRevison(agent, actions);
const config = getLatestConfigRevison(agent, actions);

await soClient.bulkUpdate<AgentSOAttributes | AgentActionSOAttributes>([
buildUpdateAgentConfigRevision(agent.id, configRevision),
...(config ? [buildUpdateAgentConfig(agent.id, config)] : []),
...buildUpdateAgentActionSentAt(actionIds),
]);

return actions;
}

function getLatestConfigRevison(agent: Agent, actions: AgentAction[]) {
return actions.reduce((acc, action) => {
return actions.reduce<null | FullAgentConfig>((acc, action) => {
if (action.type !== 'CONFIG_CHANGE') {
return acc;
}
Expand All @@ -83,16 +84,28 @@ function getLatestConfigRevison(agent: Agent, actions: AgentAction[]) {
return acc;
}

return data?.config?.revision > acc ? data?.config?.revision : acc;
}, agent.config_revision || 0);
const currentRevision = (acc && acc.revision) || agent.config_revision || 0;

return data?.config?.revision > currentRevision ? data?.config : acc;
}, null);
}

function buildUpdateAgentConfigRevision(agentId: string, configRevision: number) {
function buildUpdateAgentConfig(agentId: string, config: FullAgentConfig) {
const packages = config.inputs.reduce<string[]>((acc, input) => {
if (input.package && input.package.name) {
if (acc.indexOf(input.package.name) < 0) {
return [input.package.name, ...acc];
}
}
return acc;
}, []);

return {
type: AGENT_SAVED_OBJECT_TYPE,
id: agentId,
attributes: {
config_revision: configRevision,
config_revision: config.revision,
packages,
},
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function savedObjectToAgent(so: SavedObject<AgentSOAttributes>): Agent {
user_provided_metadata: so.attributes.user_provided_metadata,
access_api_key: undefined,
status: undefined,
packages: so.attributes.packages ?? [],
};
}

Expand Down

0 comments on commit a71f7b4

Please sign in to comment.