mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Support simplfied package policy in preconfiguration (#179087)
This commit is contained in:
parent
8fcf476cbe
commit
0092bb16aa
7 changed files with 236 additions and 94 deletions
|
@ -137,7 +137,7 @@ List of agent policies that are configured when the {fleet} app starts.
|
|||
`namespace`::::
|
||||
String identifying this policy's namespace.
|
||||
`inputs`::::
|
||||
Array that overrides any default input settings for this integration. Follows the same schema as integration inputs, with the exception that any object in `vars` can be passed `frozen: true` in order to prevent that specific `var` from being edited by the user.
|
||||
Map of input for the integration. Follows the same schema as the package policy API inputs, with the exception that any object in `vars` can be passed `frozen: true` in order to prevent that specific `var` from being edited by the user.
|
||||
=======
|
||||
=====
|
||||
+
|
||||
|
@ -151,27 +151,25 @@ xpack.fleet.packages:
|
|||
|
||||
xpack.fleet.agentPolicies:
|
||||
- name: Preconfigured Policy
|
||||
id: 1
|
||||
id: preconfigured-policy
|
||||
namespace: test
|
||||
package_policies:
|
||||
- package:
|
||||
name: system
|
||||
name: System Integration
|
||||
namespace: test
|
||||
id: preconfigured-system
|
||||
inputs:
|
||||
- type: system/metrics
|
||||
system-system/metrics:
|
||||
enabled: true
|
||||
vars:
|
||||
- name: system.hostfs
|
||||
value: home/test
|
||||
'[system.hostfs]': home/test
|
||||
streams:
|
||||
- data_stream:
|
||||
dataset: system.core
|
||||
'[system.core]':
|
||||
enabled: true
|
||||
vars:
|
||||
- name: period
|
||||
value: 20s
|
||||
- type: winlog
|
||||
period: 20s
|
||||
system-winlog:
|
||||
enabled: false
|
||||
----
|
||||
|
||||
|
|
|
@ -146,6 +146,7 @@ export function simplifiedPackagePolicytoNewPackagePolicy(
|
|||
vars: packageLevelVars,
|
||||
} = data;
|
||||
const packagePolicy = packageToPackagePolicy(packageInfo, policyId, namespace, name, description);
|
||||
|
||||
if (packagePolicy.package && options?.experimental_data_stream_features) {
|
||||
packagePolicy.package.experimental_data_stream_features =
|
||||
options.experimental_data_stream_features;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { SimplifiedPackagePolicy } from '../../services/simplified_package_policy_helper';
|
||||
|
||||
import type {
|
||||
PackagePolicyPackage,
|
||||
NewPackagePolicy,
|
||||
|
@ -24,12 +26,16 @@ export interface PreconfiguredAgentPolicy extends Omit<NewAgentPolicy, 'namespac
|
|||
id: string | number;
|
||||
namespace?: string;
|
||||
package_policies: Array<
|
||||
Partial<Omit<NewPackagePolicy, 'inputs' | 'package'>> & {
|
||||
id?: string | number;
|
||||
name: string;
|
||||
package: Partial<PackagePolicyPackage> & { name: string };
|
||||
inputs?: InputsOverride[];
|
||||
}
|
||||
| (Partial<Omit<NewPackagePolicy, 'inputs' | 'package'>> & {
|
||||
id?: string | number;
|
||||
name: string;
|
||||
package: Partial<PackagePolicyPackage> & { name: string };
|
||||
inputs?: InputsOverride[];
|
||||
})
|
||||
| (Omit<SimplifiedPackagePolicy, 'policy_id'> & {
|
||||
id: string;
|
||||
package: { name: string };
|
||||
})
|
||||
>;
|
||||
}
|
||||
|
||||
|
|
|
@ -233,6 +233,22 @@ jest.mock('./epm/packages/get', () => ({
|
|||
return {
|
||||
status: 'installed',
|
||||
...installedPackage,
|
||||
policy_templates: [
|
||||
{
|
||||
name: 'test_template',
|
||||
inputs: [
|
||||
{
|
||||
type: 'foo',
|
||||
vars: [
|
||||
{
|
||||
name: 'bar',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
getInstallation({ pkgName }: { pkgName: string }) {
|
||||
|
@ -379,6 +395,72 @@ describe('policy preconfiguration', () => {
|
|||
expect(nonFatalErrors.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should install packages and configure agent policies successfully if using simplified package policy', async () => {
|
||||
const soClient = getPutPreconfiguredPackagesMock();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
|
||||
const { policies, packages, nonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies(
|
||||
soClient,
|
||||
esClient,
|
||||
[
|
||||
{
|
||||
name: 'Test policy',
|
||||
namespace: 'default',
|
||||
id: 'test-id',
|
||||
package_policies: [
|
||||
{
|
||||
id: 'test-1',
|
||||
name: 'Test package',
|
||||
namespace: 'default',
|
||||
description: 'test',
|
||||
package: { name: 'test_package' },
|
||||
inputs: {
|
||||
'test_template-foo': {
|
||||
vars: {
|
||||
bar: 'test',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
] as PreconfiguredAgentPolicy[],
|
||||
[{ name: 'test_package', version: '3.0.0' }],
|
||||
mockDefaultOutput,
|
||||
mockDefaultDownloadService,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
expect(policies.length).toEqual(1);
|
||||
expect(policies[0].id).toBe('test-id');
|
||||
expect(packages).toEqual(expect.arrayContaining(['test_package-3.0.0']));
|
||||
expect(nonFatalErrors.length).toBe(0);
|
||||
|
||||
expect(mockedPackagePolicyService.create).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
description: 'test',
|
||||
enabled: true,
|
||||
inputs: [
|
||||
{
|
||||
enabled: true,
|
||||
policy_template: 'test_template',
|
||||
streams: [],
|
||||
type: 'foo',
|
||||
vars: { bar: { type: 'text', value: 'test' } },
|
||||
},
|
||||
],
|
||||
name: 'Test package',
|
||||
namespace: 'default',
|
||||
package: { name: 'test_package', title: 'test_package', version: '3.0.0' },
|
||||
policy_id: 'test-id',
|
||||
vars: undefined,
|
||||
}),
|
||||
expect.objectContaining({ id: 'test-1' })
|
||||
);
|
||||
});
|
||||
|
||||
it('should install prelease packages if needed', async () => {
|
||||
const soClient = getPutPreconfiguredPackagesMock();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
|
@ -869,7 +951,9 @@ describe('policy preconfiguration', () => {
|
|||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
|
||||
// Install an older version of a test package
|
||||
mockInstalledPackages.set('test_package', { version: '0.9.0' });
|
||||
mockInstalledPackages.set('test_package', {
|
||||
version: '0.9.0',
|
||||
});
|
||||
|
||||
const { policies, packages, nonFatalErrors } =
|
||||
await ensurePreconfiguredPackagesAndPolicies(
|
||||
|
|
|
@ -25,6 +25,10 @@ import type {
|
|||
import type { PreconfigurationError } from '../../common/constants';
|
||||
import { PRECONFIGURATION_LATEST_KEYWORD } from '../../common/constants';
|
||||
import { PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE } from '../constants';
|
||||
import {
|
||||
type SimplifiedPackagePolicy,
|
||||
simplifiedPackagePolicytoNewPackagePolicy,
|
||||
} from '../../common/services/simplified_package_policy_helper';
|
||||
|
||||
import { FleetError } from '../errors';
|
||||
|
||||
|
@ -34,7 +38,7 @@ import { getInstallation, getPackageInfo } from './epm/packages';
|
|||
import { ensurePackagesCompletedInstall } from './epm/packages/install';
|
||||
import { bulkInstallPackages } from './epm/packages/bulk_install_packages';
|
||||
import { agentPolicyService, addPackageToAgentPolicy } from './agent_policy';
|
||||
import type { InputsOverride } from './package_policy';
|
||||
import { type InputsOverride, packagePolicyService } from './package_policy';
|
||||
import { preconfigurePackageInputs } from './package_policy';
|
||||
import { appContextService } from './app_context';
|
||||
import type { UpgradeManagedPackagePoliciesResult } from './managed_package_policies';
|
||||
|
@ -219,7 +223,8 @@ export async function ensurePreconfiguredPackagesAndPolicies(
|
|||
true
|
||||
);
|
||||
const installedPackagePolicies = await Promise.all(
|
||||
packagePolicies.map(async ({ package: pkg, name, ...newPackagePolicy }) => {
|
||||
packagePolicies.map(async (preconfiguredPackagePolicy) => {
|
||||
const { package: pkg, ...newPackagePolicy } = preconfiguredPackagePolicy;
|
||||
const installedPackage = await getInstallation({
|
||||
savedObjectsClient: soClient,
|
||||
pkgName: pkg.name,
|
||||
|
@ -245,22 +250,23 @@ export async function ensurePreconfiguredPackagesAndPolicies(
|
|||
'[{agentPolicyName}] could not be added. [{pkgName}] is not installed, add [{pkgName}] to [{packagesConfigValue}] or remove it from [{packagePolicyName}].',
|
||||
values: {
|
||||
agentPolicyName: preconfiguredAgentPolicy.name,
|
||||
packagePolicyName: name,
|
||||
packagePolicyName: newPackagePolicy.name,
|
||||
pkgName: pkg.name,
|
||||
packagesConfigValue: 'xpack.fleet.packages',
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
return { name, installedPackage, ...newPackagePolicy };
|
||||
return { installedPackage, packagePolicy: newPackagePolicy };
|
||||
})
|
||||
);
|
||||
|
||||
const packagePoliciesToAdd = installedPackagePolicies.filter((installablePackagePolicy) => {
|
||||
return !(agentPolicyWithPackagePolicies?.package_policies as PackagePolicy[]).some(
|
||||
(packagePolicy) =>
|
||||
(packagePolicy.id !== undefined && packagePolicy.id === installablePackagePolicy.id) ||
|
||||
packagePolicy.name === installablePackagePolicy.name
|
||||
(packagePolicy.id !== undefined &&
|
||||
packagePolicy.id === installablePackagePolicy.packagePolicy.id) ||
|
||||
packagePolicy.name === installablePackagePolicy.packagePolicy.name
|
||||
);
|
||||
});
|
||||
logger.debug(`Adding preconfigured package policies ${packagePoliciesToAdd}`);
|
||||
|
@ -330,14 +336,16 @@ async function addPreconfiguredPolicyPackages(
|
|||
soClient: SavedObjectsClientContract,
|
||||
esClient: ElasticsearchClient,
|
||||
agentPolicy: AgentPolicy,
|
||||
installedPackagePolicies: Array<
|
||||
Partial<Omit<NewPackagePolicy, 'inputs'>> & {
|
||||
id?: string | number;
|
||||
name: string;
|
||||
installedPackage: Installation;
|
||||
inputs?: InputsOverride[];
|
||||
}
|
||||
>,
|
||||
installedPackagePolicies: Array<{
|
||||
installedPackage: Installation;
|
||||
packagePolicy:
|
||||
| (Partial<Omit<NewPackagePolicy, 'inputs'>> & {
|
||||
id?: string | number;
|
||||
name: string;
|
||||
inputs?: InputsOverride[];
|
||||
})
|
||||
| (Omit<SimplifiedPackagePolicy, 'package' | 'policy_id'> & { id: string });
|
||||
}>,
|
||||
defaultOutput: Output,
|
||||
bumpAgentPolicyRevison = false
|
||||
) {
|
||||
|
@ -346,9 +354,8 @@ async function addPreconfiguredPolicyPackages(
|
|||
const packageInfoMap = new Map<string, PackageInfo>();
|
||||
|
||||
// Add packages synchronously to avoid overwriting
|
||||
for (const { installedPackage, id, name, description, inputs } of installedPackagePolicies) {
|
||||
for (const { installedPackage, packagePolicy } of installedPackagePolicies) {
|
||||
let packageInfo: PackageInfo;
|
||||
|
||||
if (packageInfoMap.has(installedPackage.name)) {
|
||||
packageInfo = packageInfoMap.get(installedPackage.name)!;
|
||||
} else {
|
||||
|
@ -359,18 +366,45 @@ async function addPreconfiguredPolicyPackages(
|
|||
});
|
||||
}
|
||||
|
||||
await addPackageToAgentPolicy(
|
||||
soClient,
|
||||
esClient,
|
||||
installedPackage,
|
||||
agentPolicy,
|
||||
defaultOutput,
|
||||
packageInfo,
|
||||
name,
|
||||
id,
|
||||
description,
|
||||
(policy) => preconfigurePackageInputs(policy, packageInfo, inputs),
|
||||
bumpAgentPolicyRevison
|
||||
);
|
||||
if (Array.isArray(packagePolicy.inputs)) {
|
||||
const { id, name, description, inputs } = packagePolicy;
|
||||
await addPackageToAgentPolicy(
|
||||
soClient,
|
||||
esClient,
|
||||
installedPackage,
|
||||
agentPolicy,
|
||||
defaultOutput,
|
||||
packageInfo,
|
||||
name,
|
||||
id,
|
||||
description,
|
||||
(policy) => preconfigurePackageInputs(policy, packageInfo, inputs),
|
||||
bumpAgentPolicyRevison
|
||||
);
|
||||
} else {
|
||||
const simplifiedPackagePolicy = packagePolicy as SimplifiedPackagePolicy;
|
||||
const id = simplifiedPackagePolicy.id?.toString();
|
||||
// Simplified package policy
|
||||
const newPackagePolicy = simplifiedPackagePolicytoNewPackagePolicy(
|
||||
{
|
||||
...(simplifiedPackagePolicy as SimplifiedPackagePolicy),
|
||||
id,
|
||||
policy_id: agentPolicy.id,
|
||||
namespace: packagePolicy.namespace || agentPolicy.namespace,
|
||||
},
|
||||
packageInfo,
|
||||
{}
|
||||
);
|
||||
|
||||
await packagePolicyService.create(soClient, esClient, newPackagePolicy, {
|
||||
id,
|
||||
bumpRevision: bumpAgentPolicyRevison,
|
||||
skipEnsureInstalled: true,
|
||||
skipUniqueNameVerification: true,
|
||||
overwrite: true,
|
||||
force: true, // To add package to managed policy we need the force flag
|
||||
packageInfo,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,18 +160,11 @@ const SimplifiedVarsSchema = schema.recordOf(
|
|||
)
|
||||
);
|
||||
|
||||
export const SimplifiedCreatePackagePolicyRequestBodySchema = schema.object({
|
||||
export const SimplifiedPackagePolicyBaseSchema = schema.object({
|
||||
id: schema.maybe(schema.string()),
|
||||
name: schema.string(),
|
||||
description: schema.maybe(schema.string()),
|
||||
policy_id: schema.string(),
|
||||
namespace: schema.maybe(schema.string()),
|
||||
package: schema.object({
|
||||
name: schema.string(),
|
||||
version: schema.string(),
|
||||
experimental_data_stream_features: schema.maybe(ExperimentalDataStreamFeatures),
|
||||
}),
|
||||
force: schema.maybe(schema.boolean()),
|
||||
vars: schema.maybe(SimplifiedVarsSchema),
|
||||
inputs: schema.maybe(
|
||||
schema.recordOf(
|
||||
|
@ -193,6 +186,26 @@ export const SimplifiedCreatePackagePolicyRequestBodySchema = schema.object({
|
|||
),
|
||||
});
|
||||
|
||||
export const SimplifiedPackagePolicyPreconfiguredSchema = SimplifiedPackagePolicyBaseSchema.extends(
|
||||
{
|
||||
id: schema.string(),
|
||||
package: schema.object({
|
||||
name: schema.string(),
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
export const SimplifiedCreatePackagePolicyRequestBodySchema =
|
||||
SimplifiedPackagePolicyBaseSchema.extends({
|
||||
policy_id: schema.string(),
|
||||
force: schema.maybe(schema.boolean()),
|
||||
package: schema.object({
|
||||
name: schema.string(),
|
||||
version: schema.string(),
|
||||
experimental_data_stream_features: schema.maybe(ExperimentalDataStreamFeatures),
|
||||
}),
|
||||
});
|
||||
|
||||
export const UpdatePackagePolicyRequestBodySchema = schema.object({
|
||||
...CreatePackagePolicyProps,
|
||||
name: schema.maybe(schema.string()),
|
||||
|
|
|
@ -19,7 +19,10 @@ import {
|
|||
} from './output';
|
||||
|
||||
import { AgentPolicyBaseSchema, AgentPolicyNamespaceSchema } from './agent_policy';
|
||||
import { PackagePolicyNamespaceSchema } from './package_policy';
|
||||
import {
|
||||
PackagePolicyNamespaceSchema,
|
||||
SimplifiedPackagePolicyPreconfiguredSchema,
|
||||
} from './package_policy';
|
||||
|
||||
const varsSchema = schema.maybe(
|
||||
schema.arrayOf(
|
||||
|
@ -139,47 +142,50 @@ export const PreconfiguredAgentPoliciesSchema = schema.arrayOf(
|
|||
data_output_id: schema.maybe(schema.string()),
|
||||
monitoring_output_id: schema.maybe(schema.string()),
|
||||
package_policies: schema.arrayOf(
|
||||
schema.object({
|
||||
id: schema.maybe(schema.oneOf([schema.string(), schema.number()])),
|
||||
name: schema.string(),
|
||||
package: schema.object({
|
||||
name: schema.string({
|
||||
validate: (value) => {
|
||||
if (value === 'synthetics') {
|
||||
return i18n.translate('xpack.fleet.config.disableSynthetics', {
|
||||
defaultMessage:
|
||||
'Synthetics package is not supported via kibana.yml config. Please use Synthetics App to create monitors in private locations. https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html',
|
||||
});
|
||||
}
|
||||
},
|
||||
schema.oneOf([
|
||||
schema.object({
|
||||
id: schema.maybe(schema.oneOf([schema.string(), schema.number()])),
|
||||
name: schema.string(),
|
||||
package: schema.object({
|
||||
name: schema.string({
|
||||
validate: (value) => {
|
||||
if (value === 'synthetics') {
|
||||
return i18n.translate('xpack.fleet.config.disableSynthetics', {
|
||||
defaultMessage:
|
||||
'Synthetics package is not supported via kibana.yml config. Please use Synthetics App to create monitors in private locations. https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html',
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
description: schema.maybe(schema.string()),
|
||||
namespace: schema.maybe(PackagePolicyNamespaceSchema),
|
||||
inputs: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
type: schema.string(),
|
||||
enabled: schema.maybe(schema.boolean()),
|
||||
keep_enabled: schema.maybe(schema.boolean()),
|
||||
vars: varsSchema,
|
||||
streams: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
data_stream: schema.object({
|
||||
type: schema.maybe(schema.string()),
|
||||
dataset: schema.string(),
|
||||
}),
|
||||
enabled: schema.maybe(schema.boolean()),
|
||||
keep_enabled: schema.maybe(schema.boolean()),
|
||||
vars: varsSchema,
|
||||
})
|
||||
)
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
}),
|
||||
description: schema.maybe(schema.string()),
|
||||
namespace: schema.maybe(PackagePolicyNamespaceSchema),
|
||||
inputs: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
type: schema.string(),
|
||||
enabled: schema.maybe(schema.boolean()),
|
||||
keep_enabled: schema.maybe(schema.boolean()),
|
||||
vars: varsSchema,
|
||||
streams: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
data_stream: schema.object({
|
||||
type: schema.maybe(schema.string()),
|
||||
dataset: schema.string(),
|
||||
}),
|
||||
enabled: schema.maybe(schema.boolean()),
|
||||
keep_enabled: schema.maybe(schema.boolean()),
|
||||
vars: varsSchema,
|
||||
})
|
||||
)
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
})
|
||||
SimplifiedPackagePolicyPreconfiguredSchema,
|
||||
])
|
||||
),
|
||||
}),
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue