[Fleet] Loosen validation for experimental data stream features (#154661)

## Summary

Closes https://github.com/elastic/kibana/issues/154585

Allow `PUT`ing partial request bodies for experimental features, which
ensures we don't break package policies from prior versions of Kibana
where not all experimental features were available.

Added an FTR test to catch the error, which is an API schema validation
one. Verified the test fails before the schema changes are made.

### Checklist

Delete any items that are not applicable to this PR.

- [x] [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
This commit is contained in:
Kyle Pollich 2023-04-10 15:11:12 -04:00 committed by GitHub
parent da47a1b7a5
commit b7017b27ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 36 additions and 10 deletions

View file

@ -471,7 +471,7 @@ export type ExperimentalIndexingFeature =
export interface ExperimentalDataStreamFeature {
data_stream: string;
features: Record<ExperimentalIndexingFeature, boolean>;
features: Partial<Record<ExperimentalIndexingFeature, boolean>>;
}
export interface Installation extends SavedObjectAttributes {
@ -493,7 +493,7 @@ export interface Installation extends SavedObjectAttributes {
// TypeScript doesn't like using the `ExperimentalDataStreamFeature` type defined above here
experimental_data_stream_features?: Array<{
data_stream: string;
features: Record<ExperimentalIndexingFeature, boolean>;
features: Partial<Record<ExperimentalIndexingFeature, boolean>>;
}>;
}

View file

@ -56,7 +56,7 @@ export async function updateDatastreamExperimentalFeatures(
pkgName: string,
dataStreamFeatureMapping: Array<{
data_stream: string;
features: Record<ExperimentalIndexingFeature, boolean>;
features: Partial<Record<ExperimentalIndexingFeature, boolean>>;
}>
) {
auditLoggingService.writeCustomSoAuditLog({

View file

@ -46,7 +46,11 @@ export const applyDocOnlyValueToMapping = (
'scaled_float',
'unsigned_long',
];
if (isDocValueOnlyNumericChanged && numericTypes.includes(mapping.type ?? '')) {
if (
isDocValueOnlyNumericChanged &&
numericTypes.includes(mapping.type ?? '') &&
featureMap.features.doc_value_only_numeric !== undefined
) {
updateMapping(mapping, featureMap.features.doc_value_only_numeric);
}
@ -54,7 +58,8 @@ export const applyDocOnlyValueToMapping = (
if (
isDocValueOnlyOtherChanged &&
name !== '@timestamp' &&
otherSupportedTypes.includes(mapping.type ?? '')
otherSupportedTypes.includes(mapping.type ?? '') &&
featureMap.features.doc_value_only_other !== undefined
) {
updateMapping(mapping, featureMap.features.doc_value_only_other);
}

View file

@ -83,10 +83,10 @@ const ExperimentalDataStreamFeatures = schema.arrayOf(
schema.object({
data_stream: schema.string(),
features: schema.object({
synthetic_source: schema.boolean(),
tsdb: schema.boolean(),
doc_value_only_numeric: schema.boolean(),
doc_value_only_other: schema.boolean(),
synthetic_source: schema.maybe(schema.boolean({ defaultValue: false })),
tsdb: schema.maybe(schema.boolean({ defaultValue: false })),
doc_value_only_numeric: schema.maybe(schema.boolean({ defaultValue: false })),
doc_value_only_other: schema.maybe(schema.boolean({ defaultValue: false })),
}),
})
);

View file

@ -228,7 +228,7 @@ export default function (providerContext: FtrProviderContext) {
return resLogsDatastream.body.data_streams[0].indices.length;
}
it('should rollover datstream after enabling a expiremental datastream feature that need a rollover', async () => {
it('should rollover datstream after enabling a experimental datastream feature that need a rollover', async () => {
expect(await getLogsDefaultBackingIndicesLength()).to.be(1);
await supertest
@ -256,6 +256,27 @@ export default function (providerContext: FtrProviderContext) {
// Datastream should have been rolled over
expect(await getLogsDefaultBackingIndicesLength()).to.be(2);
});
it('should allow updating a package policy with only a partial set of experimental datastream features', async () => {
await supertest
.put(`/api/fleet/package_policies/${packagePolicyId}`)
.set('kbn-xsrf', 'xxxx')
.send({
...packagePolicyData,
package: {
...packagePolicyData.package,
experimental_data_stream_features: [
{
data_stream: logsTemplateName,
features: {
synthetic_source: true,
},
},
],
},
})
.expect(200);
});
});
});
}