Prevent future convertToMultiNamespaceType migrations (#147369)

Addresses https://github.com/elastic/kibana/issues/147344
This commit is contained in:
Gerard Soldevila 2022-12-21 18:12:54 +01:00 committed by GitHub
parent b61066d82a
commit 57dad8fc07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 25 additions and 23 deletions

View file

@ -4,6 +4,9 @@
This guide describes the "Sharing saved objects" effort, and the breaking changes that plugin developers need to be aware of for the planned
8.0 release of {kib}. It also describes how developers can take advantage of this feature.
From 8.7.0, as a step towards _zero downtime upgrades_, plugins are no longer allowed to update existing single space saved object types to become shareable.
Note that new saved object types can still be defined as `'multiple'` or `'multiple-isolated'`.
[[sharing-saved-objects-overview]]
=== Overview

View file

@ -97,6 +97,7 @@ export const expectErrorInvalidType = (obj: TypeIdTuple, overrides?: Record<stri
expectErrorResult(obj, createUnsupportedTypeErrorPayload(obj.type), overrides);
export const KIBANA_VERSION = '2.0.0';
export const ALLOWED_CONVERT_VERSION = '8.0.0';
export const CUSTOM_INDEX_TYPE = 'customIndex';
/** This type has namespaceType: 'agnostic'. */
export const NAMESPACE_AGNOSTIC_TYPE = 'globalType';
@ -372,6 +373,7 @@ export const createDocumentMigrator = (registry: SavedObjectTypeRegistry) => {
return new DocumentMigrator({
typeRegistry: registry,
kibanaVersion: KIBANA_VERSION,
convertVersion: ALLOWED_CONVERT_VERSION,
log: loggerMock.create(),
});
};

View file

@ -55,7 +55,6 @@ describe('DocumentMigrator', () => {
return {
kibanaVersion,
typeRegistry: createRegistry(),
minimumConvertVersion: '0.0.0', // no minimum version unless we specify it for a test case
log: mockLogger,
};
}
@ -63,6 +62,7 @@ describe('DocumentMigrator', () => {
describe('validation', () => {
const createDefinition = (migrations: any) => ({
kibanaVersion: '3.2.3',
convertVersion: '8.0.0',
typeRegistry: createRegistry({
name: 'foo',
migrations: migrations as any,
@ -163,7 +163,6 @@ describe('DocumentMigrator', () => {
name: 'foo',
convertToMultiNamespaceTypeVersion: 'bar',
}),
minimumConvertVersion: '0.0.0',
log: mockLogger,
};
expect(() => new DocumentMigrator(invalidDefinition)).toThrow(
@ -179,7 +178,6 @@ describe('DocumentMigrator', () => {
convertToMultiNamespaceTypeVersion: 'bar',
namespaceType: 'multiple',
}),
minimumConvertVersion: '0.0.0',
log: mockLogger,
};
expect(() => new DocumentMigrator(invalidDefinition)).toThrow(
@ -187,7 +185,7 @@ describe('DocumentMigrator', () => {
);
});
it('validates convertToMultiNamespaceTypeVersion is not less than the minimum allowed version', () => {
it('validates convertToMultiNamespaceTypeVersion matches the convertVersion, if specified', () => {
const invalidDefinition = {
kibanaVersion: '3.2.3',
typeRegistry: createRegistry({
@ -195,11 +193,11 @@ describe('DocumentMigrator', () => {
convertToMultiNamespaceTypeVersion: '3.2.4',
namespaceType: 'multiple',
}),
// not using a minimumConvertVersion parameter, the default is 8.0.0
convertVersion: '3.2.3',
log: mockLogger,
};
expect(() => new DocumentMigrator(invalidDefinition)).toThrowError(
`Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.2.4' cannot be less than '8.0.0'.`
`Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.2.4' cannot be any other than '3.2.3'.`
);
});
@ -211,7 +209,6 @@ describe('DocumentMigrator', () => {
convertToMultiNamespaceTypeVersion: '3.2.4',
namespaceType: 'multiple',
}),
minimumConvertVersion: '0.0.0',
log: mockLogger,
};
expect(() => new DocumentMigrator(invalidDefinition)).toThrowError(
@ -227,7 +224,6 @@ describe('DocumentMigrator', () => {
convertToMultiNamespaceTypeVersion: '3.1.1',
namespaceType: 'multiple',
}),
minimumConvertVersion: '0.0.0',
log: mockLogger,
};
expect(() => new DocumentMigrator(invalidDefinition)).toThrowError(

View file

@ -66,8 +66,6 @@ import {
import { MigrationLogger } from './migration_logger';
import { TransformSavedObjectDocumentError } from '.';
const DEFAULT_MINIMUM_CONVERT_VERSION = '8.0.0';
export type MigrateFn = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc;
export type MigrateAndConvertFn = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc[];
@ -95,7 +93,7 @@ interface TransformOptions {
interface DocumentMigratorOptions {
kibanaVersion: string;
typeRegistry: ISavedObjectTypeRegistry;
minimumConvertVersion?: string;
convertVersion?: string;
log: Logger;
}
@ -142,7 +140,7 @@ export interface VersionedTransformer {
* A concrete implementation of the VersionedTransformer interface.
*/
export class DocumentMigrator implements VersionedTransformer {
private documentMigratorOptions: Omit<DocumentMigratorOptions, 'minimumConvertVersion'>;
private documentMigratorOptions: Omit<DocumentMigratorOptions, 'convertVersion'>;
private migrations?: ActiveMigrations;
private transformDoc?: ApplyTransformsFn;
@ -152,17 +150,12 @@ export class DocumentMigrator implements VersionedTransformer {
* @param {DocumentMigratorOptions} opts
* @prop {string} kibanaVersion - The current version of Kibana
* @prop {SavedObjectTypeRegistry} typeRegistry - The type registry to get type migrations from
* @prop {string} minimumConvertVersion - The minimum version of Kibana in which documents can be converted to multi-namespace types
* @prop {string} convertVersion - The version of Kibana in which documents can be converted to multi-namespace types
* @prop {Logger} log - The migration logger
* @memberof DocumentMigrator
*/
constructor({
typeRegistry,
kibanaVersion,
minimumConvertVersion = DEFAULT_MINIMUM_CONVERT_VERSION,
log,
}: DocumentMigratorOptions) {
validateMigrationDefinition(typeRegistry, kibanaVersion, minimumConvertVersion);
constructor({ typeRegistry, kibanaVersion, convertVersion, log }: DocumentMigratorOptions) {
validateMigrationDefinition(typeRegistry, kibanaVersion, convertVersion);
this.documentMigratorOptions = { typeRegistry, kibanaVersion, log };
}
@ -300,7 +293,7 @@ function validateMigrationsMapObject(
function validateMigrationDefinition(
registry: ISavedObjectTypeRegistry,
kibanaVersion: string,
minimumConvertVersion: string
convertVersion?: string
) {
function assertObjectOrFunction(entity: any, prefix: string) {
if (!entity || (typeof entity !== 'function' && typeof entity !== 'object')) {
@ -321,9 +314,9 @@ function validateMigrationDefinition(
throw new Error(
`Invalid convertToMultiNamespaceTypeVersion for type ${type}. Expected value to be a semver, but got '${convertToMultiNamespaceTypeVersion}'.`
);
} else if (Semver.lt(convertToMultiNamespaceTypeVersion, minimumConvertVersion)) {
} else if (convertVersion && Semver.neq(convertToMultiNamespaceTypeVersion, convertVersion)) {
throw new Error(
`Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be less than '${minimumConvertVersion}'.`
`Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be any other than '${convertVersion}'.`
);
} else if (Semver.gt(convertToMultiNamespaceTypeVersion, kibanaVersion)) {
throw new Error(
@ -345,6 +338,7 @@ function validateMigrationDefinition(
);
}
if (convertToMultiNamespaceTypeVersion) {
// CHECKPOINT 1
assertValidConvertToMultiNamespaceType(
namespaceType,
convertToMultiNamespaceTypeVersion,

View file

@ -37,6 +37,10 @@ import { createIndexMap } from './core/build_index_map';
import { runResilientMigrator } from './run_resilient_migrator';
import { migrateRawDocsSafely } from './core/migrate_raw_docs';
// ensure plugins don't try to convert SO namespaceTypes after 8.0.0
// see https://github.com/elastic/kibana/issues/147344
const ALLOWED_CONVERT_VERSION = '8.0.0';
export interface KibanaMigratorOptions {
client: ElasticsearchClient;
typeRegistry: ISavedObjectTypeRegistry;
@ -92,6 +96,7 @@ export class KibanaMigrator implements IKibanaMigrator {
this.kibanaVersion = kibanaVersion;
this.documentMigrator = new DocumentMigrator({
kibanaVersion: this.kibanaVersion,
convertVersion: ALLOWED_CONVERT_VERSION,
typeRegistry,
log: this.log,
});

View file

@ -76,6 +76,7 @@ export interface SavedObjectMigrationContext {
readonly migrationVersion: string;
/**
* The version in which this object type is being converted to a multi-namespace type
* @deprecated Converting to multi-namespace clashes with the ZDT requirement for serverless
*/
readonly convertToMultiNamespaceTypeVersion?: string;
/**

View file

@ -113,6 +113,7 @@ export interface SavedObjectsType<Attributes = any> {
* ```
*
* Note: migration function(s) can be optionally specified for any of these versions and will not interfere with the conversion process.
* @deprecated Converting to multi-namespace clashes with the ZDT requirement for serverless
*/
convertToMultiNamespaceTypeVersion?: string;
/**