Skip to content

Commit

Permalink
[8.6] Fix SO service status when migration is disabled (elastic#145693)…
Browse files Browse the repository at this point in the history
… (elastic#145974)

# Backport

This will backport the following commits from `main` to `8.6`:
- [Fix SO service status when migration is disabled
(elastic#145693)](elastic#145693)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Pierre
Gayvallet","email":"pierre.gayvallet@elastic.co"},"sourceCommit":{"committedDate":"2022-11-21T08:02:52Z","message":"Fix
SO service status when migration is disabled (elastic#145693)\n\n##
Summary\r\n\r\nFix
https://github.com/elastic/kibana/issues/145558\r\n\r\nhave the SO
service status properly be green instead of being stuck to\r\nred when
the SO migration was skipped using `migration.skip:
true`.\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"75100868427f10ec8ed19a2bae811263c0ded311","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","release_note:skip","backport:prev-minor","v8.6.0","v8.7.0"],"number":145693,"url":"https://github.com/elastic/kibana/pull/145693","mergeCommit":{"message":"Fix
SO service status when migration is disabled (elastic#145693)\n\n##
Summary\r\n\r\nFix
https://github.com/elastic/kibana/issues/145558\r\n\r\nhave the SO
service status properly be green instead of being stuck to\r\nred when
the SO migration was skipped using `migration.skip:
true`.\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"75100868427f10ec8ed19a2bae811263c0ded311"}},"sourceBranch":"main","suggestedTargetBranches":["8.6"],"targetPullRequestStates":[{"branch":"8.6","label":"v8.6.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/145693","number":145693,"mergeCommit":{"message":"Fix
SO service status when migration is disabled (elastic#145693)\n\n##
Summary\r\n\r\nFix
https://github.com/elastic/kibana/issues/145558\r\n\r\nhave the SO
service status properly be green instead of being stuck to\r\nred when
the SO migration was skipped using `migration.skip:
true`.\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"75100868427f10ec8ed19a2bae811263c0ded311"}}]}]
BACKPORT-->
  • Loading branch information
pgayvallet authored Nov 22, 2022
1 parent 6dfa1dd commit a06f55b
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import {
registerRoutesMock,
typeRegistryInstanceMock,
} from './saved_objects_service.test.mocks';
import { BehaviorSubject } from 'rxjs';
import { RawPackageInfo, Env } from '@kbn/config';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { skip } from 'rxjs/operators';
import { type RawPackageInfo, Env } from '@kbn/config';
import { ByteSizeValue } from '@kbn/config-schema';
import { REPO_ROOT } from '@kbn/utils';
import { getEnvOptions } from '@kbn/config-mocks';
Expand Down Expand Up @@ -249,6 +250,21 @@ describe('SavedObjectsService', () => {
expect(getTypeRegistry()).toBe(typeRegistryInstanceMock);
});
});

describe('status$', () => {
it('return correct value when migration is skipped', async () => {
const coreContext = createCoreContext({ skipMigration: true });
const soService = new SavedObjectsService(coreContext);
const setup = await soService.setup(createSetupDeps());
await soService.start(createStartDeps(false));

const serviceStatus = await firstValueFrom(setup.status$.pipe(skip(1)));
expect(serviceStatus.level.toString()).toEqual('available');
expect(serviceStatus.summary).toEqual(
'SavedObjects service has completed migrations and is available'
);
});
});
});

describe('#start()', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { Subject, Observable, firstValueFrom } from 'rxjs';
import { Subject, Observable, firstValueFrom, of } from 'rxjs';
import { filter, take, switchMap } from 'rxjs/operators';
import type { Logger } from '@kbn/logging';
import type { ServiceStatus } from '@kbn/core-status-common';
Expand Down Expand Up @@ -148,9 +148,13 @@ export class SavedObjectsService

registerCoreObjectTypes(this.typeRegistry);

const skipMigration = this.config.migration.skip;

return {
status$: calculateStatus$(
this.migrator$.pipe(switchMap((migrator) => migrator.getStatus$())),
skipMigration
? of({ status: 'completed' })
: this.migrator$.pipe(switchMap((migrator) => migrator.getStatus$())),
elasticsearch.status$
),
setClientFactoryProvider: (provider) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,7 @@ export const calculateStatus$ = (
elasticsearchStatus$: Observable<ServiceStatus>
): Observable<ServiceStatus<SavedObjectStatusMeta>> => {
const migratorStatus$: Observable<ServiceStatus<SavedObjectStatusMeta>> = rawMigratorStatus$.pipe(
map((migrationStatus) => {
if (migrationStatus.status === 'waiting_to_start') {
return {
level: ServiceStatusLevels.unavailable,
summary: `SavedObjects service is waiting to start migrations`,
};
} else if (migrationStatus.status === 'waiting_for_other_nodes') {
return {
level: ServiceStatusLevels.unavailable,
summary: `SavedObjects service is waiting for other nodes to complete the migration`,
detail:
`If no other Kibana instance is attempting ` +
`migrations, you can get past this message by deleting index ${migrationStatus.waitingIndex} and ` +
`restarting Kibana.`,
};
} else if (migrationStatus.status === 'running') {
return {
level: ServiceStatusLevels.unavailable,
summary: `SavedObjects service is running migrations`,
};
}

const statusCounts: SavedObjectStatusMeta['migratedIndices'] = { migrated: 0, skipped: 0 };
if (migrationStatus.result) {
migrationStatus.result.forEach(({ status }) => {
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
});
}

return {
level: ServiceStatusLevels.available,
summary: `SavedObjects service has completed migrations and is available`,
meta: {
migratedIndices: statusCounts,
},
};
}),
map(migratorStatusToServiceStatus),
startWith({
level: ServiceStatusLevels.unavailable,
summary: `SavedObjects service is waiting to start migrations`,
Expand All @@ -80,3 +44,43 @@ export const calculateStatus$ = (
})
);
};

const migratorStatusToServiceStatus = (
migrationStatus: KibanaMigratorStatus
): ServiceStatus<SavedObjectStatusMeta> => {
if (migrationStatus.status === 'waiting_to_start') {
return {
level: ServiceStatusLevels.unavailable,
summary: `SavedObjects service is waiting to start migrations`,
};
} else if (migrationStatus.status === 'waiting_for_other_nodes') {
return {
level: ServiceStatusLevels.unavailable,
summary: `SavedObjects service is waiting for other nodes to complete the migration`,
detail:
`If no other Kibana instance is attempting ` +
`migrations, you can get past this message by deleting index ${migrationStatus.waitingIndex} and ` +
`restarting Kibana.`,
};
} else if (migrationStatus.status === 'running') {
return {
level: ServiceStatusLevels.unavailable,
summary: `SavedObjects service is running migrations`,
};
}

const statusCounts: SavedObjectStatusMeta['migratedIndices'] = { migrated: 0, skipped: 0 };
if (migrationStatus.result) {
migrationStatus.result.forEach(({ status }) => {
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
});
}

return {
level: ServiceStatusLevels.available,
summary: `SavedObjects service has completed migrations and is available`,
meta: {
migratedIndices: statusCounts,
},
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import Path from 'path';
import Fs from 'fs';
import Util from 'util';
import { firstValueFrom } from 'rxjs';
import * as kbnTestServer from '../../../../test_helpers/kbn_server';
import { Root } from '@kbn/core-root-server-internal';

const logFilePath = Path.join(__dirname, 'cleanup.log');
const asyncUnlink = Util.promisify(Fs.unlink);

async function removeLogFile() {
// ignore errors if it doesn't exist
await asyncUnlink(logFilePath).catch(() => void 0);
}

function createRoot({ skipMigration }: { skipMigration: boolean }) {
return kbnTestServer.createRootWithCorePlugins(
{
migrations: {
skip: skipMigration,
},
logging: {
appenders: {
file: {
type: 'file',
fileName: logFilePath,
layout: {
type: 'json',
},
},
},
loggers: [
{
name: 'root',
appenders: ['file'],
level: 'info',
},
],
},
},
{
oss: true,
}
);
}

describe('starting with `migration.skip: true` when indices are up to date', () => {
let esServer: kbnTestServer.TestElasticsearchUtils;
let root: Root;

beforeAll(async () => {
await removeLogFile();
});

afterAll(async () => {
if (root) {
await root.shutdown();
}
if (esServer) {
await esServer.stop();
}

await new Promise((resolve) => setTimeout(resolve, 10000));
});

it('starts and display the correct service status', async () => {
const { startES } = kbnTestServer.createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
es: {
license: 'basic',
},
},
});
esServer = await startES();

// booting root a first time to setup the indices
root = createRoot({ skipMigration: false });
await root.preboot();
await root.setup();
await root.start();
await root.shutdown();

// booting another root with migration skipped this time
root = createRoot({ skipMigration: true });
await root.preboot();
const setup = await root.setup();
await root.start();

const status = await firstValueFrom(setup.status.core$);
expect(status.savedObjects.level.toString()).toEqual('available');
expect(status.savedObjects.summary).toEqual(
'SavedObjects service has completed migrations and is available'
);
});
});

0 comments on commit a06f55b

Please sign in to comment.