diff --git a/x-pack/plugins/fleet/common/services/policy_template.ts b/x-pack/plugins/fleet/common/services/policy_template.ts index 1d523af8b05ec..912ec96f6091f 100644 --- a/x-pack/plugins/fleet/common/services/policy_template.ts +++ b/x-pack/plugins/fleet/common/services/policy_template.ts @@ -20,7 +20,7 @@ const DATA_STREAM_DATASET_VAR: RegistryVarsEntry = { type: 'text', title: 'Dataset name', description: - "Set the name for your dataset. Changing the dataset will send the data to a different index. You can't use `-` in the name of a dataset and only valid characters for [Elasticsearch index names](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html).\n", + "Set the name for your dataset. Once selected a dataset cannot be changed without creating a new integration policy. You can't use `-` in the name of a dataset and only valid characters for [Elasticsearch index names](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html) are permitted.\n", multi: false, required: true, show_user: true, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/dataset_combo.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/dataset_combo.tsx index 4588ebc4912b3..15e1a18f26dcd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/dataset_combo.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/dataset_combo.tsx @@ -13,7 +13,8 @@ export const DatasetComboBox: React.FC<{ value: any; onChange: (newValue: any) => void; datasets: string[]; -}> = ({ value, onChange, datasets }) => { + isDisabled?: boolean; +}> = ({ value, onChange, datasets, isDisabled }) => { const datasetOptions = datasets.map((dataset: string) => ({ label: dataset })) ?? []; const defaultOption = 'generic'; const [selectedOptions, setSelectedOptions] = useState>([ @@ -42,7 +43,6 @@ export const DatasetComboBox: React.FC<{ setSelectedOptions([newOption]); onChange(searchValue); }; - return ( ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx index dff5d719dc36d..fd4a3a752eacd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx @@ -29,6 +29,7 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{ updatePackagePolicyInput: (updatedInput: Partial) => void; inputVarsValidationResults: PackagePolicyConfigValidationResults; forceShowErrors?: boolean; + isEditPage?: boolean; }> = memo( ({ hasInputStreams, @@ -37,6 +38,7 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{ updatePackagePolicyInput, inputVarsValidationResults, forceShowErrors, + isEditPage = false, }) => { // Showing advanced options toggle state const [isShowingAdvanced, setIsShowingAdvanced] = useState(false); @@ -121,6 +123,7 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{ }} errors={inputVarsValidationResults.vars?.[varName]} forceShowErrors={forceShowErrors} + isEditPage={isEditPage} /> ); @@ -178,6 +181,7 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{ }} errors={inputVarsValidationResults.vars?.[varName]} forceShowErrors={forceShowErrors} + isEditPage={isEditPage} /> ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx index ccb6a6f47e1c8..68f26a3cf6121 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx @@ -80,6 +80,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{ updatePackagePolicyInput: (updatedInput: Partial) => void; inputValidationResults: PackagePolicyInputValidationResults; forceShowErrors?: boolean; + isEditPage?: boolean; }> = memo( ({ packageInput, @@ -91,6 +92,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{ updatePackagePolicyInput, inputValidationResults, forceShowErrors, + isEditPage = false, }) => { const defaultDataStreamId = useDataStreamId(); // Showing streams toggle state @@ -213,7 +215,6 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{ {/* Header rule break */} {isShowingStreams ? : null} - {/* Input level policy */} {isShowingStreams && packageInput.vars && packageInput.vars.length ? ( @@ -224,6 +225,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{ updatePackagePolicyInput={updatePackagePolicyInput} inputVarsValidationResults={{ vars: inputValidationResults?.vars }} forceShowErrors={forceShowErrors} + isEditPage={isEditPage} /> {hasInputStreams ? : } @@ -273,6 +275,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{ inputValidationResults?.streams![packagePolicyInputStream!.data_stream!.dataset] } forceShowErrors={forceShowErrors} + isEditPage={isEditPage} /> {index !== inputStreams.length - 1 ? ( <> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx index 2191b151414ba..814d1b47783f3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx @@ -58,6 +58,7 @@ interface Props { updatePackagePolicyInputStream: (updatedStream: Partial) => void; inputStreamValidationResults: PackagePolicyConfigValidationResults; forceShowErrors?: boolean; + isEditPage?: boolean; } export const PackagePolicyInputStreamConfig = memo( @@ -70,6 +71,7 @@ export const PackagePolicyInputStreamConfig = memo( updatePackagePolicyInputStream, inputStreamValidationResults, forceShowErrors, + isEditPage, }) => { const config = useConfig(); const isExperimentalDataStreamSettingsEnabled = @@ -226,6 +228,7 @@ export const PackagePolicyInputStreamConfig = memo( forceShowErrors={forceShowErrors} packageType={packageInfo.type} datasets={datasets} + isEditPage={isEditPage} /> ); @@ -287,6 +290,7 @@ export const PackagePolicyInputStreamConfig = memo( forceShowErrors={forceShowErrors} packageType={packageInfo.type} datasets={datasets} + isEditPage={isEditPage} /> ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx index e314fb2c79ca6..48b67d3078336 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx @@ -40,6 +40,7 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ frozen?: boolean; packageType?: string; datasets?: string[]; + isEditPage?: boolean; }> = memo( ({ varDef, @@ -50,6 +51,7 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ frozen, packageType, datasets = [], + isEditPage = false, }) => { const [isDirty, setIsDirty] = useState(false); const { multi, required, type, title, name, description } = varDef; @@ -68,9 +70,15 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ /> ); } - if (name === 'data_stream.dataset' && packageType === 'input') { - return ; + return ( + + ); } switch (type) { case 'textarea': @@ -152,7 +160,19 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ /> ); } - }, [isInvalid, multi, onChange, type, value, fieldLabel, frozen, datasets, name, packageType]); + }, [ + multi, + name, + packageType, + type, + value, + onChange, + frozen, + datasets, + isEditPage, + isInvalid, + fieldLabel, + ]); // Boolean cannot be optional by default set to false const isOptional = useMemo(() => type !== 'bool' && !required, [required, type]); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_configure_package.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_configure_package.tsx index 01968fd601996..edcea931b5535 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_configure_package.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_configure_package.tsx @@ -37,6 +37,7 @@ export const StepConfigurePackagePolicy: React.FunctionComponent<{ validationResults: PackagePolicyValidationResults; submitAttempted: boolean; noTopRule?: boolean; + isEditPage?: boolean; }> = ({ packageInfo, showOnlyIntegration, @@ -45,6 +46,7 @@ export const StepConfigurePackagePolicy: React.FunctionComponent<{ validationResults, submitAttempted, noTopRule = false, + isEditPage = false, }) => { const hasIntegrations = useMemo(() => doesPackageHaveIntegrations(packageInfo), [packageInfo]); const packagePolicyTemplates = useMemo( @@ -109,6 +111,7 @@ export const StepConfigurePackagePolicy: React.FunctionComponent<{ ] } forceShowErrors={submitAttempted} + isEditPage={isEditPage} /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx index be9e7425ca617..e088e00d2f95a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.test.tsx @@ -92,7 +92,7 @@ describe('StepDefinePackagePolicy', () => { let testRenderer: TestRenderer; let renderResult: ReturnType; - const render = ({ isUpdate } = { isUpdate: false }) => + const render = () => (renderResult = testRenderer.render( { updatePackagePolicy={mockUpdatePackagePolicy} validationResults={validationResults} submitAttempted={false} - isUpdate={isUpdate} /> )); @@ -165,7 +164,7 @@ describe('StepDefinePackagePolicy', () => { describe('update', () => { describe('when package vars are introduced in a new package version', () => { it('should display new package vars', () => { - render({ isUpdate: true }); + render(); waitFor(async () => { expect(renderResult.getByDisplayValue('showUserVarVal')).toBeInTheDocument(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.tsx index 186dea6c3c3da..07cebf31baebc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_define_package_policy.tsx @@ -50,23 +50,21 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{ agentPolicy?: AgentPolicy; packageInfo: PackageInfo; packagePolicy: NewPackagePolicy; - integrationToEnable?: string; updatePackagePolicy: (fields: Partial) => void; validationResults: PackagePolicyValidationResults; submitAttempted: boolean; - isUpdate?: boolean; + isEditPage?: boolean; noAdvancedToggle?: boolean; }> = memo( ({ agentPolicy, packageInfo, packagePolicy, - integrationToEnable, - isUpdate, updatePackagePolicy, validationResults, submitAttempted, noAdvancedToggle = false, + isEditPage = false, }) => { const { docLinks } = useStartServices(); @@ -251,7 +249,6 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{ )} {/* Advanced options content */} - {/* Todo: Populate list of existing namespaces */} {isShowingAdvanced ? ( @@ -266,27 +263,35 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{ /> } helpText={ - - {i18n.translate( - 'xpack.fleet.createPackagePolicy.stepConfigure.packagePolicyNamespaceHelpLearnMoreLabel', - { defaultMessage: 'Learn more' } - )} - - ), - }} - /> + isEditPage && packageInfo.type === 'input' ? ( + + ) : ( + + {i18n.translate( + 'xpack.fleet.createPackagePolicy.stepConfigure.packagePolicyNamespaceHelpLearnMoreLabel', + { defaultMessage: 'Learn more' } + )} + + ), + }} + /> + ) } > {/* Only show the out-of-box configuration step if a UI extension is NOT registered */} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 07e0636f04640..afab7a48ebb76 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -286,7 +286,7 @@ export const EditPackagePolicyForm = memo<{ updatePackagePolicy={updatePackagePolicy} validationResults={validationResults!} submitAttempted={formState === 'INVALID'} - isUpdate={true} + isEditPage={true} /> )} @@ -298,6 +298,7 @@ export const EditPackagePolicyForm = memo<{ updatePackagePolicy={updatePackagePolicy} validationResults={validationResults!} submitAttempted={formState === 'INVALID'} + isEditPage={true} /> )} diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index faf7e41f15378..ae50ec3f785f3 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -55,6 +55,7 @@ import { packagePolicyService, _applyIndexPrivileges, _compilePackagePolicyInputs, + _validateRestrictedFieldsNotModifiedOrThrow, } from './package_policy'; import { appContextService } from './app_context'; @@ -4578,3 +4579,130 @@ describe('_applyIndexPrivileges()', () => { expect(streamOut).toEqual(expectedStream); }); }); + +describe('_validateRestrictedFieldsNotModifiedOrThrow()', () => { + const pkgInfo = { + name: 'custom_logs', + title: 'Custom Logs', + version: '1.0.0', + type: 'input', + } as any as PackageInfo; + + const createInputPkgPolicy = (opts: { namespace: string; dataset: string }) => { + const { namespace, dataset } = opts; + return { + id: 'id-1234', + version: 'WzI1MywxXQ==', + name: 'custom_logs-1', + namespace, + description: '', + enabled: true, + policy_id: '1234', + revision: 1, + created_at: '2023-01-04T14:51:53.061Z', + created_by: 'elastic', + updated_at: '2023-01-04T14:51:53.061Z', + updated_by: 'elastic', + vars: {}, + inputs: [ + { + type: 'logfile', + policy_template: 'logs', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'logs', + dataset: 'custom_logs.logs', + }, + vars: { + 'data_stream.dataset': { + type: 'text', + value: dataset, + }, + }, + id: 'logfile-custom_logs.logs-1', + }, + ], + }, + ], + package: { + name: 'custom_logs', + title: 'Custom Logs', + version: '1.0.0', + }, + }; + }; + it('should not throw if restricted fields are not modified', () => { + const oldPackagePolicy = createInputPkgPolicy({ + namespace: 'default', + dataset: 'custom_logs.logs', + }); + expect(() => + _validateRestrictedFieldsNotModifiedOrThrow({ + oldPackagePolicy, + packagePolicyUpdate: oldPackagePolicy, + pkgInfo, + }) + ).not.toThrow(); + }); + + it('should throw if namespace is modified', () => { + const oldPackagePolicy = createInputPkgPolicy({ + namespace: 'default', + dataset: 'custom_logs.logs', + }); + const newPackagePolicy = createInputPkgPolicy({ + namespace: 'new-namespace', + dataset: 'custom_logs.logs', + }); + expect(() => + _validateRestrictedFieldsNotModifiedOrThrow({ + oldPackagePolicy, + packagePolicyUpdate: newPackagePolicy, + pkgInfo, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Package policy namespace cannot be modified for input only packages, please create a new package policy."` + ); + }); + + it('should throw if dataset is modified', () => { + const oldPackagePolicy = createInputPkgPolicy({ + namespace: 'default', + dataset: 'custom_logs.logs', + }); + const newPackagePolicy = createInputPkgPolicy({ + namespace: 'default', + dataset: 'new-dataset', + }); + expect(() => + _validateRestrictedFieldsNotModifiedOrThrow({ + oldPackagePolicy, + packagePolicyUpdate: newPackagePolicy, + pkgInfo, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Package policy dataset cannot be modified for input only packages, please create a new package policy."` + ); + }); + + it('should not throw if dataset is modified but package is integration package', () => { + const oldPackagePolicy = createInputPkgPolicy({ + namespace: 'default', + dataset: 'custom_logs.logs', + }); + const newPackagePolicy = createInputPkgPolicy({ + namespace: 'default', + dataset: 'new-dataset', + }); + expect(() => + _validateRestrictedFieldsNotModifiedOrThrow({ + oldPackagePolicy, + packagePolicyUpdate: newPackagePolicy, + pkgInfo: { ...pkgInfo, type: 'integration' }, + }) + ).not.toThrow(); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 0c824211ab2d5..73fda5ac50aff 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -519,7 +519,11 @@ class PackagePolicyClientImpl implements PackagePolicyClient { pkgVersion: packagePolicy.package.version, prerelease: true, }); - + _validateRestrictedFieldsNotModifiedOrThrow({ + pkgInfo, + oldPackagePolicy, + packagePolicyUpdate, + }); validatePackagePolicyOrThrow(packagePolicy, pkgInfo); inputs = await _compilePackagePolicyInputs(pkgInfo, packagePolicy.vars || {}, inputs); @@ -1950,6 +1954,52 @@ export function preconfigurePackageInputs( return resultingPackagePolicy; } +// input only packages cannot have their namespace or dataset modified +export function _validateRestrictedFieldsNotModifiedOrThrow(opts: { + pkgInfo: PackageInfo; + oldPackagePolicy: PackagePolicy; + packagePolicyUpdate: UpdatePackagePolicy; +}) { + const { pkgInfo, oldPackagePolicy, packagePolicyUpdate } = opts; + + if (pkgInfo.type !== 'input') return; + + const { namespace, inputs } = packagePolicyUpdate; + if (namespace && namespace !== oldPackagePolicy.namespace) { + throw new PackagePolicyValidationError( + i18n.translate('xpack.fleet.updatePackagePolicy.namespaceCannotBeModified', { + defaultMessage: + 'Package policy namespace cannot be modified for input only packages, please create a new package policy.', + }) + ); + } + + if (inputs) { + for (const input of inputs) { + const oldInput = oldPackagePolicy.inputs.find((i) => i.id === input.id); + if (oldInput) { + for (const stream of input.streams || []) { + const oldStream = oldInput.streams.find( + (s) => s.data_stream.dataset === stream.data_stream.dataset + ); + if ( + oldStream && + oldStream?.vars?.['data_stream.dataset'] && + oldStream?.vars['data_stream.dataset'] !== stream?.vars?.['data_stream.dataset'] + ) { + throw new PackagePolicyValidationError( + i18n.translate('xpack.fleet.updatePackagePolicy.datasetCannotBeModified', { + defaultMessage: + 'Package policy dataset cannot be modified for input only packages, please create a new package policy.', + }) + ); + } + } + } + } + } +} + async function validateIsNotHostedPolicy( soClient: SavedObjectsClientContract, id: string, diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/agent/input/input.yml.hbs b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/agent/input/input.yml.hbs new file mode 100644 index 0000000000000..1ba86fa98a2f8 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/agent/input/input.yml.hbs @@ -0,0 +1,18 @@ +paths: +{{#each paths}} + - {{this}} +{{/each}} + +{{#if tags}} +tags: +{{#each tags as |tag i|}} + - {{tag}} +{{/each}} +{{/if}} + +{{#if pipeline}} +pipeline: {{pipeline}} +{{/if}} + +data_stream: + dataset: {{data_stream.dataset}} \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/changelog.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/changelog.yml new file mode 100644 index 0000000000000..b7bc82c1696ba --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/changelog.yml @@ -0,0 +1,6 @@ +# newer versions go on top +- version: "1.0.0" + changes: + - description: basic input package + type: enhancement + link: https://github.com/elastic/package-spec/pull/325 diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/docs/README.md b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/docs/README.md new file mode 100644 index 0000000000000..9f29c89e0f5ef --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/docs/README.md @@ -0,0 +1 @@ +# Custom Logs \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/fields/input.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/fields/input.yml new file mode 100644 index 0000000000000..f5851c64b6b3a --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/fields/input.yml @@ -0,0 +1,4 @@ +- name: input.name + type: constant_keyword + description: Sample field to be added. + value: logs \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/manifest.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/manifest.yml new file mode 100644 index 0000000000000..7ca322f5f1b37 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/input_package/1.0.0/manifest.yml @@ -0,0 +1,37 @@ +format_version: 1.0.0 +name: input_package +title: Test Custom Logs Input Package +description: >- + Read lines from active log files with Elastic Agent. +type: input +version: 1.0.0 +license: basic +categories: + - custom +policy_templates: + - name: logs + type: logs + title: Custom log file + description: Collect your custom log files. + input: logfile + template_path: input.yml.hbs + vars: + - name: paths + type: text + title: Paths + multi: true + required: true + show_user: true + - name: tags + type: text + title: Tags + multi: true + required: true + show_user: false + - name: ignore_older + type: text + title: Ignore events older than + required: false + default: 72h +owner: + github: elastic/integrations \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/update.ts b/x-pack/test/fleet_api_integration/apis/package_policy/update.ts index 3a931ca09ca3d..08a0681f7cde6 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/update.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/update.ts @@ -6,10 +6,10 @@ */ import expect from '@kbn/expect'; import { policyFactory } from '@kbn/security-solution-plugin/common/endpoint/models/policy_config'; +import type { NewPackagePolicy } from '@kbn/fleet-plugin/common'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; import { testUsers } from '../test_users'; - export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const supertest = getService('supertest'); @@ -36,6 +36,10 @@ export default function (providerContext: FtrProviderContext) { let packagePolicyId2: string; let packagePolicyId3: string; let endpointPackagePolicyId: string; + let inputOnlyPackagePolicyId: string; + + let inputOnlyBasePackagePolicy: NewPackagePolicy; + before(async () => { await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); @@ -45,25 +49,21 @@ export default function (providerContext: FtrProviderContext) { if (!server.enabled) { return; } - const { body: agentPolicyResponse } = await supertest - .post(`/api/fleet/agent_policies`) - .set('kbn-xsrf', 'xxxx') - .send({ - name: 'Test policy', - namespace: 'default', - }); + const [{ body: agentPolicyResponse }, { body: managedAgentPolicyResponse }] = + await Promise.all([ + supertest.post(`/api/fleet/agent_policies`).set('kbn-xsrf', 'xxxx').send({ + name: 'Test policy', + namespace: 'default', + }), + supertest.post(`/api/fleet/agent_policies`).set('kbn-xsrf', 'xxxx').send({ + name: 'Test hosted agent policy', + namespace: 'default', + is_managed: true, + }), + ]); agentPolicyId = agentPolicyResponse.item.id; - const { body: managedAgentPolicyResponse } = await supertest - .post(`/api/fleet/agent_policies`) - .set('kbn-xsrf', 'xxxx') - .send({ - name: 'Test hosted agent policy', - namespace: 'default', - is_managed: true, - }); - // if one already exists, re-use that const managedExists = managedAgentPolicyResponse.statusCode === 409; if (managedExists) { @@ -76,90 +76,128 @@ export default function (providerContext: FtrProviderContext) { managedAgentPolicyId = managedAgentPolicyResponse.item.id; } - const { body: packagePolicyResponse } = await supertest - .post(`/api/fleet/package_policies`) - .set('kbn-xsrf', 'xxxx') - .send({ - name: 'filetest-1', - description: '', - namespace: 'default', - policy_id: agentPolicyId, - enabled: true, - inputs: [], - package: { - name: 'filetest', - title: 'For File Tests', - version: '0.1.0', - }, - }); - packagePolicyId = packagePolicyResponse.item.id; - - const { body: packagePolicyResponse2 } = await supertest - .post(`/api/fleet/package_policies`) - .set('kbn-xsrf', 'xxxx') - .send({ - name: 'filetest-2', - description: '', - namespace: 'default', - policy_id: agentPolicyId, - enabled: true, - inputs: [], - package: { - name: 'filetest', - title: 'For File Tests', - version: '0.1.0', + inputOnlyBasePackagePolicy = { + name: 'input-only-test-1', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + enabled: true, + inputs: [ + { + type: 'logfile', + policy_template: 'logs', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'input_package.logs' }, + vars: { + paths: { type: 'text', value: ['/tmp/test.log'] }, + tags: { type: 'text', value: ['tag1'] }, + ignore_older: { value: '72h', type: 'text' }, + 'data_stream.dataset': { type: 'text', value: 'generic' }, + }, + }, + ], }, - }); - packagePolicyId2 = packagePolicyResponse2.item.id; + ], + package: { name: 'input_package', title: 'Input only package', version: '1.0.0' }, + }; - const { body: packagePolicyResponse3 } = await supertest - .post(`/api/fleet/package_policies`) - .set('kbn-xsrf', 'xxxx') - .send({ - name: 'update-package-policy-with_required_variables-1', - description: '', - namespace: 'default', - policy_id: agentPolicyId, - inputs: { - 'with_required_variables-test_input': { - streams: { - 'with_required_variables.log': { - vars: { test_var_required: 'I am required' }, + const [ + { body: packagePolicyResponse }, + { body: packagePolicyResponse2 }, + { body: packagePolicyResponse3 }, + { body: endpointPackagePolicyResponse }, + { body: inputOnlyPolicyResponse }, + ] = await Promise.all([ + supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'filetest-1', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + enabled: true, + inputs: [], + package: { + name: 'filetest', + title: 'For File Tests', + version: '0.1.0', + }, + }), + supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'filetest-2', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + enabled: true, + inputs: [], + package: { + name: 'filetest', + title: 'For File Tests', + version: '0.1.0', + }, + }), + supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'update-package-policy-with_required_variables-1', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + inputs: { + 'with_required_variables-test_input': { + streams: { + 'with_required_variables.log': { + vars: { test_var_required: 'I am required' }, + }, }, }, }, - }, - package: { - name: 'with_required_variables', - version: '0.1.0', - }, - }); - packagePolicyId3 = packagePolicyResponse3.item.id; - - const { body: endpointPackagePolicyResponse } = await supertest - .post(`/api/fleet/package_policies`) - .set('kbn-xsrf', 'xxxx') - .send({ - name: 'endpoint-1', - description: '', - namespace: 'default', - policy_id: agentPolicyId, - enabled: true, - inputs: [ - { - enabled: true, - streams: [], - type: 'endpoint', + package: { + name: 'with_required_variables', + version: '0.1.0', }, - ], - force: true, - package: { - name: 'endpoint', - title: 'Elastic Defend', - version: '8.6.1', - }, - }); + }), + supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'endpoint-1', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + enabled: true, + inputs: [ + { + enabled: true, + streams: [], + type: 'endpoint', + }, + ], + force: true, + package: { + name: 'endpoint', + title: 'Elastic Defend', + version: '8.6.1', + }, + }), + supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send(inputOnlyBasePackagePolicy), + ]); + packagePolicyId = packagePolicyResponse.item.id; + packagePolicyId2 = packagePolicyResponse2.item.id; + packagePolicyId3 = packagePolicyResponse3.item.id; endpointPackagePolicyId = endpointPackagePolicyResponse.item.id; + inputOnlyPackagePolicyId = inputOnlyPolicyResponse.item.id; }); after(async function () { @@ -167,12 +205,14 @@ export default function (providerContext: FtrProviderContext) { .post(`/api/fleet/agent_policies/delete`) .set('kbn-xsrf', 'xxxx') .send({ agentPolicyId }); - // uninstall endpoint package await supertest .delete(`/api/fleet/epm/packages/endpoint-8.6.1`) .set('kbn-xsrf', 'xxxx') - .send({ force: true }) + .expect(200); + await supertest + .delete(`/api/fleet/epm/packages/input_package-1.0.0`) + .set('kbn-xsrf', 'xxxx') .expect(200); }); @@ -438,6 +478,34 @@ export default function (providerContext: FtrProviderContext) { .expect(400); expect(body.message).eql('Input not found: with_required_variables-i-do-not-exists'); }); + + it('should return a 400 if namespace is edited on input only package policy', async function () { + const { body } = await supertest + .put(`/api/fleet/package_policies/${inputOnlyPackagePolicyId}`) + .set('kbn-xsrf', 'xxxx') + .send({ + ...inputOnlyBasePackagePolicy, + namespace: 'updated_namespace', + }) + .expect(400); + expect(body.message).eql( + 'Package policy namespace cannot be modified for input only packages, please create a new package policy.' + ); + }); + it('should return a 400 if dataset is edited on input only package policy', async function () { + const updatedPolicy = JSON.parse(JSON.stringify(inputOnlyBasePackagePolicy)); + + updatedPolicy.inputs[0].streams[0].vars['data_stream.dataset'].value = 'updated_dataset'; + + const { body } = await supertest + .put(`/api/fleet/package_policies/${inputOnlyPackagePolicyId}`) + .set('kbn-xsrf', 'xxxx') + .send(updatedPolicy) + .expect(400); + expect(body.message).eql( + 'Package policy dataset cannot be modified for input only packages, please create a new package policy.' + ); + }); }); }); }