Skip to content

Commit

Permalink
[Fleet] Add UI and mappings for agent policy unenroll_timeout (#102970)
Browse files Browse the repository at this point in the history
## Summary

closes #100617

UI and mappings related to ephemeral agents

- [x] Adds mapping/type/schema definition for the new field in agent policy saved object
- [x] Shows input field labelled `Unenrollment timeout` in agent policy settings that reads/writes to the new field
- [x] Same input in `Advanced options` section of create agent flyout
- [x] `unenroll_timeout` can be set using preconfigured agent policies defined in `kibana.yml`
- [x] `unenroll_timeout` can be populated if the user has a preconfigured policy that _does not_ have this field initially, but then updates their `kibana.yml` later to include it

<details><summary>Screenshot - editing an existing agent policy</summary>

<img width="1251" alt="Screen Shot 2021-06-22 at 1 42 50 PM" src="https://user-images.githubusercontent.com/57655/122974924-d479b800-d360-11eb-8d37-306a6860f140.png">

</details>


<details><summary>Screenshots - adding a new agent policy</summary>
<img width="1258" alt="Screen Shot 2021-06-22 at 1 45 01 PM" src="https://user-images.githubusercontent.com/57655/122975141-0be86480-d361-11eb-9ccb-dde0cc44e438.png">
<img width="1257" alt="Screen Shot 2021-06-22 at 1 45 35 PM" src="https://user-images.githubusercontent.com/57655/122975139-0be86480-d361-11eb-90ae-3541dbbd68e6.png">
<img width="1258" alt="Screen Shot 2021-06-22 at 1 45 44 PM" src="https://user-images.githubusercontent.com/57655/122975138-0b4fce00-d361-11eb-8347-d6f0cb43ab91.png">
<img width="1254" alt="Screen Shot 2021-06-22 at 1 45 56 PM" src="https://user-images.githubusercontent.com/57655/122975136-0b4fce00-d361-11eb-9c51-ec7a9e52ea2b.png">
</details>

<details><summary>Using <code>kibana.dev.yml</code></summary>

<h3>No <code>unenroll_timeout</code></h3>

```yml
xpack.fleet.agentPolicies:
  - name: Preconfigured Policy From Config
    description: From kibana.dev.yml (no timeout given)
    id: 1
    namespace: test
    package_policies:
      - package:
          name: system
        name: System Integration
        inputs:
          - type: system/metrics
            enabled: true
            vars:
              - name: system.hostfs
                value: home/test
            streams:
              - data_stream:
                  dataset: system.core
                enabled: true
                vars:
                  - name: period
                    value: 20s
          - type: winlog
            enabled: false
```

<h4>UI (saved object)</h4>
<img width="1072" alt="Screen Shot 2021-06-23 at 10 28 03 AM" src="https://user-images.githubusercontent.com/57655/123117442-bf0f9700-d40f-11eb-8e8f-685fee3ccb4b.png">

<h4><code>fleet-policies</code>index</h4>
<img width="1690" alt="Screen Shot 2021-06-23 at 10 52 39 AM" src="https://user-images.githubusercontent.com/57655/123119430-883a8080-d411-11eb-990f-4953845a76b5.png">


<h4>Updated <code>kibana.dev.yml</code> to include <code>unenroll_timeout</code></h4>

```yml
xpack.fleet.agentPolicies:
  - name: Preconfigured Policy From Config
    description: From kibana.dev.yml (updated with timeout)
    id: 1
    namespace: test
    unenroll_timeout: 234
    package_policies:
      - package:
          name: system
        name: System Integration
        inputs:
          - type: system/metrics
            enabled: true
            vars:
              - name: system.hostfs
                value: home/test
            streams:
              - data_stream:
                  dataset: system.core
                enabled: true
                vars:
                  - name: period
                    value: 20s
          - type: winlog
            enabled: false
```

<h4>UI (saved object)</h4>
<img width="1073" alt="Screen Shot 2021-06-23 at 10 35 17 AM" src="https://user-images.githubusercontent.com/57655/123117444-bfa82d80-d40f-11eb-94a9-99d4ca4bed2a.png">

<h4><code>fleet-policies</code>index</h4>
<img width="1690" alt="Screen Shot 2021-06-23 at 10 35 41 AM" src="https://user-images.githubusercontent.com/57655/123117446-bfa82d80-d40f-11eb-9563-1add80f43f0e.png">

</details>

### Checklist
- [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
  • Loading branch information
John Schulz authored Jun 23, 2021
1 parent 874dfc6 commit bb7bff5
Show file tree
Hide file tree
Showing 13 changed files with 52 additions and 12 deletions.
3 changes: 3 additions & 0 deletions x-pack/plugins/fleet/common/constants/epm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ export const dataTypes = {
Metrics: 'metrics',
} as const;

// currently identical but may be a subset or otherwise different some day
export const monitoringTypes = Object.values(dataTypes);

export const installationStatuses = {
Installed: 'installed',
NotInstalled: 'not_installed',
Expand Down
5 changes: 3 additions & 2 deletions x-pack/plugins/fleet/common/constants/preconfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
FLEET_SYSTEM_PACKAGE,
FLEET_SERVER_PACKAGE,
autoUpdatePackages,
monitoringTypes,
} from './epm';

export const PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE =
Expand Down Expand Up @@ -40,7 +41,7 @@ export const DEFAULT_AGENT_POLICY: PreconfiguredAgentPolicyWithDefaultInputs = {
],
is_default: true,
is_managed: false,
monitoring_enabled: ['logs', 'metrics'] as Array<'logs' | 'metrics'>,
monitoring_enabled: monitoringTypes,
};

export const DEFAULT_FLEET_SERVER_AGENT_POLICY: PreconfiguredAgentPolicyWithDefaultInputs = {
Expand All @@ -58,7 +59,7 @@ export const DEFAULT_FLEET_SERVER_AGENT_POLICY: PreconfiguredAgentPolicyWithDefa
is_default: false,
is_default_fleet_server: true,
is_managed: false,
monitoring_enabled: ['logs', 'metrics'] as Array<'logs' | 'metrics'>,
monitoring_enabled: monitoringTypes,
};

export const DEFAULT_PACKAGES = defaultPackages.map((name) => ({
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/fleet/common/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,5 +117,5 @@ export const INSTALL_SCRIPT_API_ROUTES = `${API_ROOT}/install/{osType}`;

// Policy preconfig API routes
export const PRECONFIGURATION_API_ROUTES = {
PUT_PRECONFIG: `${API_ROOT}/setup/preconfiguration`,
UPDATE_PATTERN: `${API_ROOT}/setup/preconfiguration`,
};
9 changes: 7 additions & 2 deletions x-pack/plugins/fleet/common/types/models/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import type { agentPolicyStatuses } from '../../constants';
import type { DataType, ValueOf } from '../../types';
import type { MonitoringType, ValueOf } from '../../types';

import type { PackagePolicy, PackagePolicyPackage } from './package_policy';
import type { Output } from './output';
Expand All @@ -20,7 +20,8 @@ export interface NewAgentPolicy {
is_default?: boolean;
is_default_fleet_server?: boolean; // Optional when creating a policy
is_managed?: boolean; // Optional when creating a policy
monitoring_enabled?: Array<ValueOf<DataType>>;
monitoring_enabled?: MonitoringType;
unenroll_timeout?: number;
is_preconfigured?: boolean;
}

Expand Down Expand Up @@ -138,4 +139,8 @@ export interface FleetServerPolicy {
* True when this policy is the default policy to start Fleet Server
*/
default_fleet_server: boolean;
/**
* Auto unenroll any Elastic Agents which have not checked in for this many seconds
*/
unenroll_timeout?: number;
}
3 changes: 2 additions & 1 deletion x-pack/plugins/fleet/common/types/models/epm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
ASSETS_SAVED_OBJECT_TYPE,
agentAssetTypes,
dataTypes,
monitoringTypes,
installationStatuses,
} from '../../constants';
import type { ValueOf } from '../../types';
Expand Down Expand Up @@ -92,7 +93,7 @@ export enum ElasticsearchAssetType {
}

export type DataType = typeof dataTypes;

export type MonitoringType = typeof monitoringTypes;
export type InstallablePackage = RegistryPackage | ArchivePackage;

export type ArchivePackage = PackageSpecManifest &
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
EuiCheckboxGroup,
EuiButton,
EuiLink,
EuiFieldNumber,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
Expand Down Expand Up @@ -158,6 +159,10 @@ export const AgentPolicyForm: React.FunctionComponent<Props> = ({
</EuiFormRow>
);
});
const unenrollmentTimeoutText = i18n.translate(
'xpack.fleet.agentPolicyForm.unenrollmentTimeoutLabel',
{ defaultMessage: 'Unenrollment timeout' }
);

const advancedOptionsContent = (
<>
Expand Down Expand Up @@ -297,6 +302,27 @@ export const AgentPolicyForm: React.FunctionComponent<Props> = ({
}}
/>
</EuiDescribedFormGroup>
<EuiDescribedFormGroup
title={<h4>{unenrollmentTimeoutText}</h4>}
description={
<FormattedMessage
id="xpack.fleet.agentPolicyForm.unenrollmentTimeoutDescription"
defaultMessage="An optional timeout in seconds. If provided, an agent will automatically unenroll after being gone for this period of time."
/>
}
>
<EuiFormRow fullWidth>
<EuiFieldNumber
fullWidth
value={agentPolicy.unenroll_timeout}
min={1}
onChange={(e) => updateAgentPolicy({ unenroll_timeout: Number(e.target.value) })}
isInvalid={Boolean(touchedFields.unenroll_timeout && validation.unenroll_timeout)}
onBlur={() => setTouchedFields({ ...touchedFields, unenroll_timeout: true })}
placeholder={unenrollmentTimeoutText}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
{isEditing &&
'id' in agentPolicy &&
!agentPolicy.is_managed &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>(
setIsLoading(true);
try {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { name, description, namespace, monitoring_enabled } = agentPolicy;
const { name, description, namespace, monitoring_enabled, unenroll_timeout } = agentPolicy;
const { data, error } = await sendUpdateAgentPolicy(agentPolicy.id, {
name,
description,
namespace,
monitoring_enabled,
unenroll_timeout,
});
if (data) {
notifications.toasts.addSuccess(
Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/fleet/server/routes/preconfiguration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { PutPreconfigurationSchema } from '../../types';
import { defaultIngestErrorHandler } from '../../errors';
import { ensurePreconfiguredPackagesAndPolicies, outputService } from '../../services';

export const putPreconfigurationHandler: RequestHandler<
export const updatePreconfigurationHandler: RequestHandler<
undefined,
undefined,
TypeOf<typeof PutPreconfigurationSchema.body>
Expand Down Expand Up @@ -43,10 +43,10 @@ export const putPreconfigurationHandler: RequestHandler<
export const registerRoutes = (router: IRouter) => {
router.put(
{
path: PRECONFIGURATION_API_ROUTES.PUT_PRECONFIG,
path: PRECONFIGURATION_API_ROUTES.UPDATE_PATTERN,
validate: PutPreconfigurationSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
putPreconfigurationHandler
updatePreconfigurationHandler
);
};
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/saved_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const getSavedObjectTypes = (
is_managed: { type: 'boolean' },
status: { type: 'keyword' },
package_policies: { type: 'keyword' },
unenroll_timeout: { type: 'integer' },
updated_at: { type: 'date' },
updated_by: { type: 'keyword' },
revision: { type: 'integer' },
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/services/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,7 @@ class AgentPolicyService {
data: (fullPolicy as unknown) as FleetServerPolicy['data'],
policy_id: fullPolicy.id,
default_fleet_server: policy.is_default_fleet_server === true,
unenroll_timeout: policy.unenroll_timeout,
};

await esClient.create({
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/fleet/server/services/preconfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export async function ensurePreconfiguredPackagesAndPolicies(
policies.map(async (preconfiguredAgentPolicy) => {
if (preconfiguredAgentPolicy.id) {
// Check to see if a preconfigured policy with the same preconfiguration id was already deleted by the user
const preconfigurationId = String(preconfiguredAgentPolicy.id);
const preconfigurationId = preconfiguredAgentPolicy.id.toString();
const searchParams = {
searchFields: ['id'],
search: escapeSearchQueryPhrase(preconfigurationId),
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/types/models/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const AgentPolicyBaseSchema = {
namespace: NamespaceSchema,
description: schema.maybe(schema.string()),
is_managed: schema.maybe(schema.boolean()),
unenroll_timeout: schema.maybe(schema.number({ min: 1 })),
monitoring_enabled: schema.maybe(
schema.arrayOf(
schema.oneOf([schema.literal(dataTypes.Logs), schema.literal(dataTypes.Metrics)])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function (providerContext: FtrProviderContext) {
// Basic health check for the API; functionality is covered by the unit tests
it('should succeed with an empty payload', async () => {
const { body } = await supertest
.put(PRECONFIGURATION_API_ROUTES.PUT_PRECONFIG)
.put(PRECONFIGURATION_API_ROUTES.UPDATE_PATTERN)
.set('kbn-xsrf', 'xxxx')
.send({})
.expect(200);
Expand Down

0 comments on commit bb7bff5

Please sign in to comment.