[Upgrade Assistant] Create new status endpoint (#105998)

This commit is contained in:
Alison Goryachev 2021-08-16 08:50:36 -04:00 committed by GitHub
parent 8a571c2f7d
commit 650f45b823
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 606 additions and 109 deletions

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [DeprecationsClient](./kibana-plugin-core-server.deprecationsclient.md) &gt; [getAllDeprecations](./kibana-plugin-core-server.deprecationsclient.getalldeprecations.md)
## DeprecationsClient.getAllDeprecations property
<b>Signature:</b>
```typescript
getAllDeprecations: () => Promise<DomainDeprecationDetails[]>;
```

View file

@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [DeprecationsClient](./kibana-plugin-core-server.deprecationsclient.md)
## DeprecationsClient interface
Server-side client that provides access to fetch all Kibana deprecations
<b>Signature:</b>
```typescript
export interface DeprecationsClient
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [getAllDeprecations](./kibana-plugin-core-server.deprecationsclient.getalldeprecations.md) | <code>() =&gt; Promise&lt;DomainDeprecationDetails[]&gt;</code> | |

View file

@ -71,6 +71,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [DeprecationAPIClientParams](./kibana-plugin-core-server.deprecationapiclientparams.md) | |
| [DeprecationAPIResponse](./kibana-plugin-core-server.deprecationapiresponse.md) | |
| [DeprecationInfo](./kibana-plugin-core-server.deprecationinfo.md) | |
| [DeprecationsClient](./kibana-plugin-core-server.deprecationsclient.md) | Server-side client that provides access to fetch all Kibana deprecations |
| [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) | |
| [DeprecationSettings](./kibana-plugin-core-server.deprecationsettings.md) | UiSettings deprecation field options. |
| [DeprecationsServiceSetup](./kibana-plugin-core-server.deprecationsservicesetup.md) | The deprecations service provides a way for the Kibana platform to communicate deprecated features and configs with its users. These deprecations are only communicated if the deployment is using these features. Allowing for a user tailored experience for upgrading the stack version.<!-- -->The Deprecation service is consumed by the upgrade assistant to assist with the upgrade experience.<!-- -->If a deprecated feature can be resolved without manual user intervention. Using correctiveActions.api allows the Upgrade Assistant to use this api to correct the deprecation upon a user trigger. |

View file

@ -24,5 +24,8 @@ core: {
uiSettings: {
client: IUiSettingsClient;
};
deprecations: {
client: DeprecationsClient;
};
};
```

View file

@ -18,5 +18,5 @@ export interface RequestHandlerContext
| Property | Type | Description |
| --- | --- | --- |
| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | <code>{</code><br/><code> savedObjects: {</code><br/><code> client: SavedObjectsClientContract;</code><br/><code> typeRegistry: ISavedObjectTypeRegistry;</code><br/><code> getClient: (options?: SavedObjectsClientProviderOptions) =&gt; SavedObjectsClientContract;</code><br/><code> getExporter: (client: SavedObjectsClientContract) =&gt; ISavedObjectsExporter;</code><br/><code> getImporter: (client: SavedObjectsClientContract) =&gt; ISavedObjectsImporter;</code><br/><code> };</code><br/><code> elasticsearch: {</code><br/><code> client: IScopedClusterClient;</code><br/><code> legacy: {</code><br/><code> client: ILegacyScopedClusterClient;</code><br/><code> };</code><br/><code> };</code><br/><code> uiSettings: {</code><br/><code> client: IUiSettingsClient;</code><br/><code> };</code><br/><code> }</code> | |
| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | <code>{</code><br/><code> savedObjects: {</code><br/><code> client: SavedObjectsClientContract;</code><br/><code> typeRegistry: ISavedObjectTypeRegistry;</code><br/><code> getClient: (options?: SavedObjectsClientProviderOptions) =&gt; SavedObjectsClientContract;</code><br/><code> getExporter: (client: SavedObjectsClientContract) =&gt; ISavedObjectsExporter;</code><br/><code> getImporter: (client: SavedObjectsClientContract) =&gt; ISavedObjectsImporter;</code><br/><code> };</code><br/><code> elasticsearch: {</code><br/><code> client: IScopedClusterClient;</code><br/><code> legacy: {</code><br/><code> client: ILegacyScopedClusterClient;</code><br/><code> };</code><br/><code> };</code><br/><code> uiSettings: {</code><br/><code> client: IUiSettingsClient;</code><br/><code> };</code><br/><code> deprecations: {</code><br/><code> client: DeprecationsClient;</code><br/><code> };</code><br/><code> }</code> | |

View file

@ -192,3 +192,40 @@ describe('#uiSettings', () => {
});
});
});
describe('#deprecations', () => {
describe('#client', () => {
test('returns the results of coreStart.deprecations.asScopedToClient', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);
const client = context.deprecations.client;
expect(client).toBe(coreStart.deprecations.asScopedToClient.mock.results[0].value);
});
test('lazily created', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);
expect(coreStart.deprecations.asScopedToClient).not.toHaveBeenCalled();
const client = context.deprecations.client;
expect(coreStart.deprecations.asScopedToClient).toHaveBeenCalled();
expect(client).toBeDefined();
});
test('only creates one instance', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);
const client1 = context.deprecations.client;
const client2 = context.deprecations.client;
expect(coreStart.deprecations.asScopedToClient.mock.calls.length).toBe(1);
const mockResult = coreStart.deprecations.asScopedToClient.mock.results[0].value;
expect(client1).toBe(mockResult);
expect(client2).toBe(mockResult);
});
});
});

View file

@ -21,6 +21,7 @@ import {
LegacyScopedClusterClient,
} from './elasticsearch';
import { InternalUiSettingsServiceStart, IUiSettingsClient } from './ui_settings';
import { DeprecationsClient, InternalDeprecationsServiceStart } from './deprecations';
class CoreElasticsearchRouteHandlerContext {
#client?: IScopedClusterClient;
@ -103,10 +104,30 @@ class CoreUiSettingsRouteHandlerContext {
}
}
class CoreDeprecationsRouteHandlerContext {
#client?: DeprecationsClient;
constructor(
private readonly deprecationsStart: InternalDeprecationsServiceStart,
private readonly elasticsearchRouterHandlerContext: CoreElasticsearchRouteHandlerContext,
private readonly savedObjectsRouterHandlerContext: CoreSavedObjectsRouteHandlerContext
) {}
public get client() {
if (this.#client == null) {
this.#client = this.deprecationsStart.asScopedToClient(
this.elasticsearchRouterHandlerContext.client,
this.savedObjectsRouterHandlerContext.client
);
}
return this.#client;
}
}
export class CoreRouteHandlerContext {
readonly elasticsearch: CoreElasticsearchRouteHandlerContext;
readonly savedObjects: CoreSavedObjectsRouteHandlerContext;
readonly uiSettings: CoreUiSettingsRouteHandlerContext;
readonly deprecations: CoreDeprecationsRouteHandlerContext;
constructor(
private readonly coreStart: InternalCoreStart,
@ -124,5 +145,10 @@ export class CoreRouteHandlerContext {
this.coreStart.uiSettings,
this.savedObjects
);
this.deprecations = new CoreDeprecationsRouteHandlerContext(
this.coreStart.deprecations,
this.elasticsearch,
this.savedObjects
);
}
}

View file

@ -11,6 +11,8 @@ import {
DeprecationsService,
InternalDeprecationsServiceSetup,
DeprecationsServiceSetup,
InternalDeprecationsServiceStart,
DeprecationsClient,
} from './deprecations_service';
type DeprecationsServiceContract = PublicMethodsOf<DeprecationsService>;
@ -22,6 +24,16 @@ const createSetupContractMock = () => {
return setupContract;
};
const createStartContractMock = () => {
const mocked: jest.Mocked<InternalDeprecationsServiceStart> = {
asScopedToClient: jest.fn(),
};
mocked.asScopedToClient.mockReturnValue(createClientMock());
return mocked;
};
const createInternalSetupContractMock = () => {
const internalSetupContract: jest.Mocked<InternalDeprecationsServiceSetup> = {
getRegistry: jest.fn(),
@ -42,8 +54,18 @@ const createDeprecationsServiceMock = () => {
return mocked;
};
const createClientMock = () => {
const mocked: jest.Mocked<DeprecationsClient> = {
getAllDeprecations: jest.fn(),
};
mocked.getAllDeprecations.mockResolvedValue([]);
return mocked;
};
export const deprecationsServiceMock = {
create: createDeprecationsServiceMock,
createInternalSetupContract: createInternalSetupContractMock,
createSetupContract: createSetupContractMock,
createInternalStartContract: createStartContractMock,
createClient: createClientMock,
};

View file

@ -10,20 +10,21 @@
import { DeprecationsService } from './deprecations_service';
import { httpServiceMock } from '../http/http_service.mock';
import { mockRouter } from '../http/router/router.mock';
import { savedObjectsClientMock, elasticsearchServiceMock } from '../mocks';
import { mockCoreContext } from '../core_context.mock';
import { mockDeprecationsFactory } from './deprecations_factory.mock';
import { mockDeprecationsRegistry } from './deprecations_registry.mock';
describe('DeprecationsService', () => {
const coreContext = mockCoreContext.create();
const http = httpServiceMock.createInternalSetupContract();
const router = mockRouter.create();
http.createRouter.mockReturnValue(router);
const deprecationsCoreSetupDeps = { http };
beforeEach(() => jest.clearAllMocks());
describe('#setup', () => {
const http = httpServiceMock.createInternalSetupContract();
const router = mockRouter.create();
http.createRouter.mockReturnValue(router);
const deprecationsCoreSetupDeps = { http };
it('registers routes', () => {
const deprecationsService = new DeprecationsService(coreContext);
deprecationsService.setup(deprecationsCoreSetupDeps);
@ -43,6 +44,23 @@ describe('DeprecationsService', () => {
});
});
describe('#start', () => {
describe('#asScopedToClient', () => {
it('returns client with #getAllDeprecations method', async () => {
const esClient = elasticsearchServiceMock.createScopedClusterClient();
const savedObjectsClient = savedObjectsClientMock.create();
const deprecationsService = new DeprecationsService(coreContext);
deprecationsService.setup(deprecationsCoreSetupDeps);
const start = deprecationsService.start();
const deprecationsClient = start.asScopedToClient(esClient, savedObjectsClient);
expect(deprecationsClient.getAllDeprecations).toBeDefined();
});
});
});
describe('#registerConfigDeprecationsInfo', () => {
const deprecationsFactory = mockDeprecationsFactory.create();
const deprecationsRegistry = mockDeprecationsRegistry.create();

View file

@ -7,13 +7,15 @@
*/
import { DeprecationsFactory } from './deprecations_factory';
import { RegisterDeprecationsConfig } from './types';
import { DomainDeprecationDetails, RegisterDeprecationsConfig } from './types';
import { registerRoutes } from './routes';
import { CoreContext } from '../core_context';
import { CoreService } from '../../types';
import { InternalHttpServiceSetup } from '../http';
import { Logger } from '../logging';
import { IScopedClusterClient } from '../elasticsearch/client';
import { SavedObjectsClientContract } from '../saved_objects/types';
/**
* The deprecations service provides a way for the Kibana platform to communicate deprecated
@ -102,6 +104,25 @@ export interface DeprecationsServiceSetup {
registerDeprecations: (deprecationContext: RegisterDeprecationsConfig) => void;
}
/**
* Server-side client that provides access to fetch all Kibana deprecations
*
* @public
*/
export interface DeprecationsClient {
getAllDeprecations: () => Promise<DomainDeprecationDetails[]>;
}
export interface InternalDeprecationsServiceStart {
/**
* Creates a {@link DeprecationsClient} with provided SO client and ES client.
*
*/
asScopedToClient(
esClient: IScopedClusterClient,
savedObjectsClient: SavedObjectsClientContract
): DeprecationsClient;
}
/** @internal */
export interface InternalDeprecationsServiceSetup {
getRegistry: (domainId: string) => DeprecationsServiceSetup;
@ -113,21 +134,24 @@ export interface DeprecationsSetupDeps {
}
/** @internal */
export class DeprecationsService implements CoreService<InternalDeprecationsServiceSetup> {
export class DeprecationsService
implements CoreService<InternalDeprecationsServiceSetup, InternalDeprecationsServiceStart> {
private readonly logger: Logger;
private readonly deprecationsFactory: DeprecationsFactory;
constructor(private readonly coreContext: Pick<CoreContext, 'logger' | 'configService'>) {
this.logger = coreContext.logger.get('deprecations-service');
this.deprecationsFactory = new DeprecationsFactory({
logger: this.logger,
});
}
public setup({ http }: DeprecationsSetupDeps): InternalDeprecationsServiceSetup {
this.logger.debug('Setting up Deprecations service');
const deprecationsFactory = new DeprecationsFactory({
logger: this.logger,
});
const deprecationsFactory = this.deprecationsFactory;
registerRoutes({ http, deprecationsFactory });
this.registerConfigDeprecationsInfo(deprecationsFactory);
registerRoutes({ http });
this.registerConfigDeprecationsInfo(this.deprecationsFactory);
return {
getRegistry: (domainId: string): DeprecationsServiceSetup => {
@ -139,9 +163,28 @@ export class DeprecationsService implements CoreService<InternalDeprecationsServ
};
}
public start() {}
public start(): InternalDeprecationsServiceStart {
return {
asScopedToClient: this.createScopedDeprecations(),
};
}
public stop() {}
private createScopedDeprecations(): (
esClient: IScopedClusterClient,
savedObjectsClient: SavedObjectsClientContract
) => DeprecationsClient {
return (esClient: IScopedClusterClient, savedObjectsClient: SavedObjectsClientContract) => {
return {
getAllDeprecations: this.deprecationsFactory.getAllDeprecations.bind(null, {
savedObjectsClient,
esClient,
}),
};
};
}
private registerConfigDeprecationsInfo(deprecationsFactory: DeprecationsFactory) {
const handledDeprecatedConfigs = this.coreContext.configService.getHandledDeprecatedConfigs();

View file

@ -16,6 +16,8 @@ export type {
export type {
DeprecationsServiceSetup,
InternalDeprecationsServiceSetup,
InternalDeprecationsServiceStart,
DeprecationsClient,
} from './deprecations_service';
export { DeprecationsService } from './deprecations_service';

View file

@ -6,27 +6,19 @@
* Side Public License, v 1.
*/
import { IRouter } from '../../http';
import { GetDeprecationsContext, DeprecationsGetResponse } from '../types';
import { DeprecationsFactory } from '../deprecations_factory';
import { DeprecationsGetResponse } from '../types';
interface RouteDependencies {
deprecationsFactory: DeprecationsFactory;
}
export const registerGetRoute = (router: IRouter, { deprecationsFactory }: RouteDependencies) => {
export const registerGetRoute = (router: IRouter) => {
router.get(
{
path: '/',
validate: false,
},
async (context, req, res) => {
const dependencies: GetDeprecationsContext = {
esClient: context.core.elasticsearch.client,
savedObjectsClient: context.core.savedObjects.client,
};
const deprecationsClient = context.core.deprecations.client;
const body: DeprecationsGetResponse = {
deprecations: await deprecationsFactory.getAllDeprecations(dependencies),
deprecations: await deprecationsClient.getAllDeprecations(),
};
return res.ok({ body });

View file

@ -8,15 +8,8 @@
import { InternalHttpServiceSetup } from '../../http';
import { registerGetRoute } from './get';
import { DeprecationsFactory } from '../deprecations_factory';
export function registerRoutes({
http,
deprecationsFactory,
}: {
http: InternalHttpServiceSetup;
deprecationsFactory: DeprecationsFactory;
}) {
export function registerRoutes({ http }: { http: InternalHttpServiceSetup }) {
const router = http.createRouter('/api/deprecations');
registerGetRoute(router, { deprecationsFactory });
registerGetRoute(router);
}

View file

@ -58,7 +58,7 @@ import { StatusServiceSetup } from './status';
import { AppenderConfigType, appendersSchema, LoggingServiceSetup } from './logging';
import { CoreUsageDataStart } from './core_usage_data';
import { I18nServiceSetup } from './i18n';
import { DeprecationsServiceSetup } from './deprecations';
import { DeprecationsServiceSetup, DeprecationsClient } from './deprecations';
// Because of #79265 we need to explicitly import, then export these types for
// scripts/telemetry_check.js to work as expected
import {
@ -408,6 +408,7 @@ export type {
RegisterDeprecationsConfig,
GetDeprecationsContext,
DeprecationsServiceSetup,
DeprecationsClient,
} from './deprecations';
export type { AppCategory } from '../types';
@ -471,6 +472,9 @@ export interface RequestHandlerContext {
uiSettings: {
client: IUiSettingsClient;
};
deprecations: {
client: DeprecationsClient;
};
};
}

View file

@ -38,7 +38,7 @@ import { InternalStatusServiceSetup } from './status';
import { InternalLoggingServicePreboot, InternalLoggingServiceSetup } from './logging';
import { CoreUsageDataStart } from './core_usage_data';
import { I18nServiceSetup } from './i18n';
import { InternalDeprecationsServiceSetup } from './deprecations';
import { InternalDeprecationsServiceSetup, InternalDeprecationsServiceStart } from './deprecations';
import type {
InternalExecutionContextSetup,
InternalExecutionContextStart,
@ -87,6 +87,7 @@ export interface InternalCoreStart {
uiSettings: InternalUiSettingsServiceStart;
coreUsageData: CoreUsageDataStart;
executionContext: InternalExecutionContextStart;
deprecations: InternalDeprecationsServiceStart;
}
/**

View file

@ -236,6 +236,7 @@ function createInternalCoreStartMock() {
uiSettings: uiSettingsServiceMock.createStartContract(),
coreUsageData: coreUsageDataServiceMock.createStartContract(),
executionContext: executionContextServiceMock.createInternalStartContract(),
deprecations: deprecationsServiceMock.createInternalStartContract(),
};
return startDeps;
}
@ -258,6 +259,9 @@ function createCoreRequestHandlerContextMock() {
uiSettings: {
client: uiSettingsServiceMock.createClient(),
},
deprecations: {
client: deprecationsServiceMock.createClient(),
},
};
}

View file

@ -894,6 +894,14 @@ export interface DeprecationInfo {
url: string;
}
// @public
export interface DeprecationsClient {
// Warning: (ae-forgotten-export) The symbol "DomainDeprecationDetails" needs to be exported by the entry point index.d.ts
//
// (undocumented)
getAllDeprecations: () => Promise<DomainDeprecationDetails[]>;
}
// Warning: (ae-missing-release-tag) "DeprecationsDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@ -2102,6 +2110,9 @@ export interface RequestHandlerContext {
uiSettings: {
client: IUiSettingsClient;
};
deprecations: {
client: DeprecationsClient;
};
};
}

View file

@ -101,3 +101,9 @@ export const mockPrebootService = prebootServiceMock.create();
jest.doMock('./preboot/preboot_service', () => ({
PrebootService: jest.fn(() => mockPrebootService),
}));
import { deprecationsServiceMock } from './deprecations/deprecations_service.mock';
export const mockDeprecationService = deprecationsServiceMock.create();
jest.doMock('./deprecations/deprecations_service', () => ({
DeprecationsService: jest.fn(() => mockDeprecationService),
}));

View file

@ -23,6 +23,7 @@ import {
mockI18nService,
mockEnvironmentService,
mockPrebootService,
mockDeprecationService,
} from './server.test.mocks';
import { BehaviorSubject } from 'rxjs';
@ -102,6 +103,7 @@ test('sets up services on "setup"', async () => {
expect(mockStatusService.setup).not.toHaveBeenCalled();
expect(mockLoggingService.setup).not.toHaveBeenCalled();
expect(mockI18nService.setup).not.toHaveBeenCalled();
expect(mockDeprecationService.setup).not.toHaveBeenCalled();
await server.setup();
@ -117,6 +119,7 @@ test('sets up services on "setup"', async () => {
expect(mockStatusService.setup).toHaveBeenCalledTimes(1);
expect(mockLoggingService.setup).toHaveBeenCalledTimes(1);
expect(mockI18nService.setup).toHaveBeenCalledTimes(1);
expect(mockDeprecationService.setup).toHaveBeenCalledTimes(1);
});
test('injects legacy dependency to context#setup()', async () => {
@ -166,6 +169,7 @@ test('runs services on "start"', async () => {
expect(mockUiSettingsService.start).not.toHaveBeenCalled();
expect(mockMetricsService.start).not.toHaveBeenCalled();
expect(mockStatusService.start).not.toHaveBeenCalled();
expect(mockDeprecationService.start).not.toHaveBeenCalled();
await server.start();
@ -174,6 +178,7 @@ test('runs services on "start"', async () => {
expect(mockUiSettingsService.start).toHaveBeenCalledTimes(1);
expect(mockMetricsService.start).toHaveBeenCalledTimes(1);
expect(mockStatusService.start).toHaveBeenCalledTimes(1);
expect(mockDeprecationService.start).toHaveBeenCalledTimes(1);
});
test('does not fail on "setup" if there are unused paths detected', async () => {

View file

@ -317,6 +317,7 @@ export class Server {
savedObjects: savedObjectsStart,
exposedConfigsToUsage: this.plugins.getExposedPluginConfigsToUsage(),
});
const deprecationsStart = this.deprecations.start();
this.status.start();
this.coreStart = {
@ -328,6 +329,7 @@ export class Server {
savedObjects: savedObjectsStart,
uiSettings: uiSettingsStart,
coreUsageData: coreUsageDataStart,
deprecations: deprecationsStart,
};
await this.plugins.start(this.coreStart);

View file

@ -6,7 +6,7 @@
*/
import { act } from 'react-dom/test-utils';
import { MlAction, UpgradeAssistantStatus } from '../../common/types';
import { MlAction, ESUpgradeStatus } from '../../common/types';
import { ClusterTestBed, setupClusterPage, setupEnvironment } from './helpers';
@ -21,8 +21,8 @@ describe('Cluster tab', () => {
describe('with deprecations', () => {
const snapshotId = '1';
const jobId = 'deprecation_check_job';
const upgradeStatusMockResponse: UpgradeAssistantStatus = {
readyForUpgrade: false,
const esDeprecationsMockResponse: ESUpgradeStatus = {
totalCriticalDeprecations: 1,
cluster: [
{
level: 'critical',
@ -42,7 +42,7 @@ describe('Cluster tab', () => {
};
beforeEach(async () => {
httpRequestsMockHelpers.setLoadEsDeprecationsResponse(upgradeStatusMockResponse);
httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse);
httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ isEnabled: true });
await act(async () => {
@ -79,7 +79,7 @@ describe('Cluster tab', () => {
actions.clickExpandAll();
// The data-test-subj is derived from the deprecation message
const accordionTestSubj = `depgroup_${upgradeStatusMockResponse.cluster[0].message
const accordionTestSubj = `depgroup_${esDeprecationsMockResponse.cluster[0].message
.split(' ')
.join('_')}`;
@ -155,7 +155,7 @@ describe('Cluster tab', () => {
expect(upgradeRequest.method).toBe('POST');
expect(upgradeRequest.url).toBe('/api/upgrade_assistant/ml_snapshots');
const accordionTestSubj = `depgroup_${upgradeStatusMockResponse.cluster[0].message
const accordionTestSubj = `depgroup_${esDeprecationsMockResponse.cluster[0].message
.split(' ')
.join('_')}`;
@ -180,7 +180,7 @@ describe('Cluster tab', () => {
component.update();
const request = server.requests[server.requests.length - 1];
const mlDeprecation = upgradeStatusMockResponse.cluster[0];
const mlDeprecation = esDeprecationsMockResponse.cluster[0];
expect(request.method).toBe('DELETE');
expect(request.url).toBe(
@ -212,7 +212,7 @@ describe('Cluster tab', () => {
component.update();
const request = server.requests[server.requests.length - 1];
const mlDeprecation = upgradeStatusMockResponse.cluster[0];
const mlDeprecation = esDeprecationsMockResponse.cluster[0];
expect(request.method).toBe('DELETE');
expect(request.url).toBe(
@ -221,7 +221,7 @@ describe('Cluster tab', () => {
}/${(mlDeprecation.correctiveAction! as MlAction).snapshotId}`
);
const accordionTestSubj = `depgroup_${upgradeStatusMockResponse.cluster[0].message
const accordionTestSubj = `depgroup_${esDeprecationsMockResponse.cluster[0].message
.split(' ')
.join('_')}`;
@ -233,7 +233,7 @@ describe('Cluster tab', () => {
describe('no deprecations', () => {
beforeEach(async () => {
const noDeprecationsResponse = {
readyForUpgrade: false,
totalCriticalDeprecations: 0,
cluster: [],
indices: [],
};

View file

@ -7,19 +7,16 @@
import sinon, { SinonFakeServer } from 'sinon';
import { API_BASE_PATH } from '../../../common/constants';
import { UpgradeAssistantStatus } from '../../../common/types';
import { ESUpgradeStatus } from '../../../common/types';
import { ResponseError } from '../../../public/application/lib/api';
// Register helpers to mock HTTP Requests
const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const setLoadEsDeprecationsResponse = (
response?: UpgradeAssistantStatus,
error?: ResponseError
) => {
const setLoadEsDeprecationsResponse = (response?: ESUpgradeStatus, error?: ResponseError) => {
const status = error ? error.statusCode || 400 : 200;
const body = error ? error : response;
server.respondWith('GET', `${API_BASE_PATH}/status`, [
server.respondWith('GET', `${API_BASE_PATH}/es_deprecations`, [
status,
{ 'Content-Type': 'application/json' },
JSON.stringify(body),

View file

@ -7,7 +7,7 @@
import { act } from 'react-dom/test-utils';
import { indexSettingDeprecations } from '../../common/constants';
import { UpgradeAssistantStatus } from '../../common/types';
import { ESUpgradeStatus } from '../../common/types';
import { IndicesTestBed, setupIndicesPage, setupEnvironment } from './helpers';
@ -20,8 +20,8 @@ describe('Indices tab', () => {
});
describe('with deprecations', () => {
const upgradeStatusMockResponse: UpgradeAssistantStatus = {
readyForUpgrade: false,
const esDeprecationsMockResponse: ESUpgradeStatus = {
totalCriticalDeprecations: 0,
cluster: [],
indices: [
{
@ -38,7 +38,7 @@ describe('Indices tab', () => {
};
beforeEach(async () => {
httpRequestsMockHelpers.setLoadEsDeprecationsResponse(upgradeStatusMockResponse);
httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse);
httpRequestsMockHelpers.setLoadDeprecationLoggingResponse({ isEnabled: true });
await act(async () => {
@ -93,7 +93,7 @@ describe('Indices tab', () => {
expect(modal).not.toBe(null);
expect(modal!.textContent).toContain('Remove deprecated settings');
const indexName = upgradeStatusMockResponse.indices[0].index;
const indexName = esDeprecationsMockResponse.indices[0].index;
httpRequestsMockHelpers.setUpdateIndexSettingsResponse({
acknowledged: true,
@ -117,7 +117,7 @@ describe('Indices tab', () => {
describe('no deprecations', () => {
beforeEach(async () => {
const noDeprecationsResponse = {
readyForUpgrade: false,
totalCriticalDeprecations: 0,
cluster: [],
indices: [],
};

View file

@ -8,7 +8,7 @@
import type { DomainDeprecationDetails } from 'kibana/public';
import { act } from 'react-dom/test-utils';
import { deprecationsServiceMock } from 'src/core/public/mocks';
import { UpgradeAssistantStatus } from '../../common/types';
import { ESUpgradeStatus } from '../../common/types';
import { OverviewTestBed, setupOverviewPage, setupEnvironment } from './helpers';
@ -17,8 +17,8 @@ describe('Overview page', () => {
const { server, httpRequestsMockHelpers } = setupEnvironment();
beforeEach(async () => {
const esDeprecationsMockResponse: UpgradeAssistantStatus = {
readyForUpgrade: false,
const esDeprecationsMockResponse: ESUpgradeStatus = {
totalCriticalDeprecations: 1,
cluster: [
{
level: 'critical',

View file

@ -220,8 +220,8 @@ export interface EnrichedDeprecationInfo extends DeprecationInfo {
correctiveAction?: ReindexAction | MlAction | IndexSettingAction;
}
export interface UpgradeAssistantStatus {
readyForUpgrade: boolean;
export interface ESUpgradeStatus {
totalCriticalDeprecations: number;
cluster: EnrichedDeprecationInfo[];
indices: EnrichedDeprecationInfo[];
}

View file

@ -7,12 +7,12 @@
import React from 'react';
import { EnrichedDeprecationInfo, UpgradeAssistantStatus } from '../../../common/types';
import { EnrichedDeprecationInfo, ESUpgradeStatus } from '../../../common/types';
import { ResponseError } from '../lib/api';
export interface UpgradeAssistantTabProps {
alertBanner?: React.ReactNode;
checkupData?: UpgradeAssistantStatus | null;
checkupData?: ESUpgradeStatus | null;
deprecations?: EnrichedDeprecationInfo[];
refreshCheckupData: () => void;
error: ResponseError | null;

View file

@ -6,7 +6,7 @@
*/
import { HttpSetup } from 'src/core/public';
import { UpgradeAssistantStatus } from '../../../common/types';
import { ESUpgradeStatus } from '../../../common/types';
import { API_BASE_PATH } from '../../../common/constants';
import {
UseRequestConfig,
@ -46,8 +46,8 @@ export class ApiService {
}
public useLoadUpgradeStatus() {
return this.useRequest<UpgradeAssistantStatus>({
path: `${API_BASE_PATH}/status`,
return this.useRequest<ESUpgradeStatus>({
path: `${API_BASE_PATH}/es_deprecations`,
method: 'get',
});
}

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`getUpgradeAssistantStatus returns the correct shape of data 1`] = `
exports[`getESUpgradeStatus returns the correct shape of data 1`] = `
Object {
"cluster": Array [
Object {
@ -129,6 +129,6 @@ Object {
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
],
"readyForUpgrade": false,
"totalCriticalDeprecations": 4,
}
`;

View file

@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`getKibanaUpgradeStatus returns the correct shape of data 1`] = `
Object {
"totalCriticalDeprecations": 1,
}
`;

View file

@ -10,7 +10,7 @@ import { RequestEvent } from '@elastic/elasticsearch/lib/Transport';
import { elasticsearchServiceMock } from 'src/core/server/mocks';
import { DeprecationAPIResponse } from '../../common/types';
import { getUpgradeAssistantStatus } from './es_migration_apis';
import { getESUpgradeStatus } from './es_deprecations_status';
import fakeDeprecations from './__fixtures__/fake_deprecations.json';
const fakeIndexNames = Object.keys(fakeDeprecations.index_settings);
@ -20,7 +20,7 @@ const asApiResponse = <T>(body: T): RequestEvent<T> =>
body,
} as RequestEvent<T>);
describe('getUpgradeAssistantStatus', () => {
describe('getESUpgradeStatus', () => {
const resolvedIndices = {
indices: fakeIndexNames.map((indexName) => {
// mark one index as closed to test blockerForReindexing flag
@ -45,16 +45,16 @@ describe('getUpgradeAssistantStatus', () => {
esClient.asCurrentUser.indices.resolveIndex.mockResolvedValue(asApiResponse(resolvedIndices));
it('calls /_migration/deprecations', async () => {
await getUpgradeAssistantStatus(esClient);
await getESUpgradeStatus(esClient);
expect(esClient.asCurrentUser.migration.deprecations).toHaveBeenCalled();
});
it('returns the correct shape of data', async () => {
const resp = await getUpgradeAssistantStatus(esClient);
const resp = await getESUpgradeStatus(esClient);
expect(resp).toMatchSnapshot();
});
it('returns readyForUpgrade === false when critical issues found', async () => {
it('returns totalCriticalDeprecations > 0 when critical issues found', async () => {
esClient.asCurrentUser.migration.deprecations.mockResolvedValue(
// @ts-expect-error not full interface
asApiResponse({
@ -65,13 +65,13 @@ describe('getUpgradeAssistantStatus', () => {
})
);
await expect(getUpgradeAssistantStatus(esClient)).resolves.toHaveProperty(
'readyForUpgrade',
false
await expect(getESUpgradeStatus(esClient)).resolves.toHaveProperty(
'totalCriticalDeprecations',
1
);
});
it('returns readyForUpgrade === true when no critical issues found', async () => {
it('returns totalCriticalDeprecations === 0 when no critical issues found', async () => {
esClient.asCurrentUser.migration.deprecations.mockResolvedValue(
// @ts-expect-error not full interface
asApiResponse({
@ -82,9 +82,9 @@ describe('getUpgradeAssistantStatus', () => {
})
);
await expect(getUpgradeAssistantStatus(esClient)).resolves.toHaveProperty(
'readyForUpgrade',
true
await expect(getESUpgradeStatus(esClient)).resolves.toHaveProperty(
'totalCriticalDeprecations',
0
);
});
});

View file

@ -10,23 +10,24 @@ import { indexSettingDeprecations } from '../../common/constants';
import {
DeprecationAPIResponse,
EnrichedDeprecationInfo,
UpgradeAssistantStatus,
ESUpgradeStatus,
} from '../../common/types';
import { esIndicesStateCheck } from './es_indices_state_check';
export async function getUpgradeAssistantStatus(
export async function getESUpgradeStatus(
dataClient: IScopedClusterClient
): Promise<UpgradeAssistantStatus> {
): Promise<ESUpgradeStatus> {
const { body: deprecations } = await dataClient.asCurrentUser.migration.deprecations();
const cluster = getClusterDeprecations(deprecations);
const indices = await getCombinedIndexInfos(deprecations, dataClient);
const criticalWarnings = cluster.concat(indices).filter((d) => d.level === 'critical');
const totalCriticalDeprecations = cluster.concat(indices).filter((d) => d.level === 'critical')
.length;
return {
readyForUpgrade: criticalWarnings.length === 0,
totalCriticalDeprecations,
cluster,
indices,
};

View file

@ -0,0 +1,58 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import _ from 'lodash';
import { deprecationsServiceMock } from 'src/core/server/mocks';
import { DomainDeprecationDetails } from 'src/core/server/types';
import { getKibanaUpgradeStatus } from './kibana_status';
const mockKibanaDeprecations: DomainDeprecationDetails[] = [
{
correctiveActions: {
manualSteps: [
'Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.',
'Using Kibana role-mapping management, change all role-mappings which assing the kibana_user role to the kibana_admin role.',
],
},
deprecationType: 'config',
documentationUrl: 'testDocUrl',
level: 'critical',
message: 'testMessage',
requireRestart: true,
domainId: 'security',
},
];
describe('getKibanaUpgradeStatus', () => {
const deprecationsClient = deprecationsServiceMock.createClient();
deprecationsClient.getAllDeprecations.mockResolvedValue(mockKibanaDeprecations);
it('returns the correct shape of data', async () => {
const resp = await getKibanaUpgradeStatus(deprecationsClient);
expect(resp).toMatchSnapshot();
});
it('returns totalCriticalDeprecations > 0 when critical issues found', async () => {
deprecationsClient.getAllDeprecations.mockResolvedValue(mockKibanaDeprecations);
await expect(getKibanaUpgradeStatus(deprecationsClient)).resolves.toHaveProperty(
'totalCriticalDeprecations',
1
);
});
it('returns totalCriticalDeprecations === 0 when no critical issues found', async () => {
deprecationsClient.getAllDeprecations.mockResolvedValue([]);
await expect(getKibanaUpgradeStatus(deprecationsClient)).resolves.toHaveProperty(
'totalCriticalDeprecations',
0
);
});
});

View file

@ -0,0 +1,19 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { DeprecationsClient } from 'kibana/server';
import { DomainDeprecationDetails } from 'src/core/server/types';
export const getKibanaUpgradeStatus = async (deprecationsClient: DeprecationsClient) => {
const kibanaDeprecations: DomainDeprecationDetails[] = await deprecationsClient.getAllDeprecations();
const totalCriticalDeprecations = kibanaDeprecations.filter((d) => d.level === 'critical').length;
return {
totalCriticalDeprecations,
};
};

View file

@ -94,13 +94,13 @@ export class UpgradeAssistantServerPlugin implements Plugin {
router,
credentialStore: this.credentialStore,
log: this.logger,
licensing,
getSavedObjectsService: () => {
if (!this.savedObjectsServiceStart) {
throw new Error('Saved Objects Start service not available');
}
return this.savedObjectsServiceStart;
},
licensing,
};
// Initialize version service with current kibana version

View file

@ -9,6 +9,7 @@ import { RequestHandler, RequestHandlerContext } from 'src/core/server';
import {
elasticsearchServiceMock,
savedObjectsClientMock,
deprecationsServiceMock,
} from '../../../../../../src/core/server/mocks';
export const routeHandlerContextMock = ({
@ -17,6 +18,7 @@ export const routeHandlerContextMock = ({
client: elasticsearchServiceMock.createScopedClusterClient(),
},
savedObjects: { client: savedObjectsClientMock.create() },
deprecations: { client: deprecationsServiceMock.createClient() },
},
} as unknown) as RequestHandlerContext;

View file

@ -15,17 +15,17 @@ jest.mock('../lib/es_version_precheck', () => ({
// Need to require to get mock on named export to work.
// eslint-disable-next-line @typescript-eslint/no-var-requires
const MigrationApis = require('../lib/es_migration_apis');
MigrationApis.getUpgradeAssistantStatus = jest.fn();
const ESUpgradeStatusApis = require('../lib/es_deprecations_status');
ESUpgradeStatusApis.getESUpgradeStatus = jest.fn();
import { registerClusterCheckupRoutes } from './cluster_checkup';
import { registerESDeprecationRoutes } from './es_deprecations';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the es_migration_apis test.
* more thoroughly in the es_deprecations_status test.
*/
describe('cluster checkup API', () => {
describe('ES deprecations API', () => {
let mockRouter: MockRouter;
let routeDependencies: any;
@ -34,23 +34,23 @@ describe('cluster checkup API', () => {
routeDependencies = {
router: mockRouter,
};
registerClusterCheckupRoutes(routeDependencies);
registerESDeprecationRoutes(routeDependencies);
});
afterEach(() => {
jest.resetAllMocks();
});
describe('GET /api/upgrade_assistant/reindex/{indexName}.json', () => {
describe('GET /api/upgrade_assistant/es_deprecations', () => {
it('returns state', async () => {
MigrationApis.getUpgradeAssistantStatus.mockResolvedValue({
ESUpgradeStatusApis.getESUpgradeStatus.mockResolvedValue({
cluster: [],
indices: [],
nodes: [],
});
const resp = await routeDependencies.router.getHandler({
method: 'get',
pathPattern: '/api/upgrade_assistant/status',
pathPattern: '/api/upgrade_assistant/es_deprecations',
})(routeHandlerContextMock, createRequestMock(), kibanaResponseFactory);
expect(resp.status).toEqual(200);
@ -63,22 +63,22 @@ describe('cluster checkup API', () => {
const e: any = new Error(`you can't go here!`);
e.statusCode = 403;
MigrationApis.getUpgradeAssistantStatus.mockRejectedValue(e);
ESUpgradeStatusApis.getESUpgradeStatus.mockRejectedValue(e);
const resp = await routeDependencies.router.getHandler({
method: 'get',
pathPattern: '/api/upgrade_assistant/status',
pathPattern: '/api/upgrade_assistant/es_deprecations',
})(routeHandlerContextMock, createRequestMock(), kibanaResponseFactory);
expect(resp.status).toEqual(403);
});
it('returns an 500 error if it throws', async () => {
MigrationApis.getUpgradeAssistantStatus.mockRejectedValue(new Error(`scary error!`));
ESUpgradeStatusApis.getESUpgradeStatus.mockRejectedValue(new Error('scary error!'));
await expect(
routeDependencies.router.getHandler({
method: 'get',
pathPattern: '/api/upgrade_assistant/status',
pathPattern: '/api/upgrade_assistant/es_deprecations',
})(routeHandlerContextMock, createRequestMock(), kibanaResponseFactory)
).rejects.toThrow('scary error!');
});

View file

@ -6,16 +6,16 @@
*/
import { API_BASE_PATH } from '../../common/constants';
import { getUpgradeAssistantStatus } from '../lib/es_migration_apis';
import { getESUpgradeStatus } from '../lib/es_deprecations_status';
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
import { RouteDependencies } from '../types';
import { reindexActionsFactory } from '../lib/reindexing/reindex_actions';
import { reindexServiceFactory } from '../lib/reindexing';
export function registerClusterCheckupRoutes({ router, licensing, log }: RouteDependencies) {
export function registerESDeprecationRoutes({ router, licensing, log }: RouteDependencies) {
router.get(
{
path: `${API_BASE_PATH}/status`,
path: `${API_BASE_PATH}/es_deprecations`,
validate: false,
},
versionCheckHandlerWrapper(
@ -30,7 +30,7 @@ export function registerClusterCheckupRoutes({ router, licensing, log }: RouteDe
response
) => {
try {
const status = await getUpgradeAssistantStatus(client);
const status = await getESUpgradeStatus(client);
const asCurrentUser = client.asCurrentUser;
const reindexActions = reindexActionsFactory(savedObjectsClient, asCurrentUser);

View file

@ -7,19 +7,22 @@
import { RouteDependencies } from '../types';
import { registerClusterCheckupRoutes } from './cluster_checkup';
import { registerESDeprecationRoutes } from './es_deprecations';
import { registerDeprecationLoggingRoutes } from './deprecation_logging';
import { registerReindexIndicesRoutes } from './reindex_indices';
import { registerTelemetryRoutes } from './telemetry';
import { registerUpdateSettingsRoute } from './update_index_settings';
import { registerMlSnapshotRoutes } from './ml_snapshots';
import { ReindexWorker } from '../lib/reindexing';
import { registerUpgradeStatusRoute } from './status';
export function registerRoutes(dependencies: RouteDependencies, getWorker: () => ReindexWorker) {
registerClusterCheckupRoutes(dependencies);
registerESDeprecationRoutes(dependencies);
registerDeprecationLoggingRoutes(dependencies);
registerReindexIndicesRoutes(dependencies, getWorker);
registerTelemetryRoutes(dependencies);
registerUpdateSettingsRoute(dependencies);
registerMlSnapshotRoutes(dependencies);
// Route for cloud to retrieve the upgrade status for ES and Kibana
registerUpgradeStatusRoute(dependencies);
}

View file

@ -0,0 +1,119 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { kibanaResponseFactory } from 'src/core/server';
import { createMockRouter, MockRouter, routeHandlerContextMock } from './__mocks__/routes.mock';
import { createRequestMock } from './__mocks__/request.mock';
import { registerUpgradeStatusRoute } from './status';
jest.mock('../lib/es_version_precheck', () => ({
versionCheckHandlerWrapper: (a: any) => a,
}));
// Need to require to get mock on named export to work.
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ESUpgradeStatusApis = require('../lib/es_deprecations_status');
ESUpgradeStatusApis.getESUpgradeStatus = jest.fn();
// eslint-disable-next-line @typescript-eslint/no-var-requires
const KibanaUpgradeStatusApis = require('../lib/kibana_status');
KibanaUpgradeStatusApis.getKibanaUpgradeStatus = jest.fn();
describe('Status API', () => {
let mockRouter: MockRouter;
let routeDependencies: any;
beforeEach(() => {
mockRouter = createMockRouter();
routeDependencies = {
router: mockRouter,
};
registerUpgradeStatusRoute(routeDependencies);
});
afterEach(() => {
jest.resetAllMocks();
});
describe('GET /api/upgrade_assistant/status', () => {
it('returns readyForUpgrade === false if Kibana or ES contain critical deprecations', async () => {
ESUpgradeStatusApis.getESUpgradeStatus.mockResolvedValue({
cluster: [
{
level: 'critical',
message:
'model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded',
details:
'model snapshot [%s] for job [%s] supports minimum version [%s] and needs to be at least [%s]',
url: 'doc_url',
correctiveAction: {
type: 'mlSnapshot',
snapshotId: '1',
jobId: 'deprecation_check_job',
},
},
],
indices: [],
totalCriticalDeprecations: 1,
});
KibanaUpgradeStatusApis.getKibanaUpgradeStatus.mockResolvedValue({
totalCriticalDeprecations: 1,
});
const resp = await routeDependencies.router.getHandler({
method: 'get',
pathPattern: '/api/upgrade_assistant/status',
})(routeHandlerContextMock, createRequestMock(), kibanaResponseFactory);
expect(resp.status).toEqual(200);
expect(resp.payload).toEqual({
readyForUpgrade: false,
details:
'You have 1 Elasticsearch deprecation issues and 1 Kibana deprecation issues that must be resolved before upgrading.',
});
});
it('returns readyForUpgrade === true if there are no critical deprecations', async () => {
ESUpgradeStatusApis.getESUpgradeStatus.mockResolvedValue({
cluster: [],
indices: [],
totalCriticalDeprecations: 0,
});
KibanaUpgradeStatusApis.getKibanaUpgradeStatus.mockResolvedValue({
totalCriticalDeprecations: 0,
});
const resp = await routeDependencies.router.getHandler({
method: 'get',
pathPattern: '/api/upgrade_assistant/status',
})(routeHandlerContextMock, createRequestMock(), kibanaResponseFactory);
expect(resp.status).toEqual(200);
expect(resp.payload).toEqual({
readyForUpgrade: true,
details: 'All deprecation issues have been resolved.',
});
});
it('returns an error if it throws', async () => {
ESUpgradeStatusApis.getESUpgradeStatus.mockRejectedValue(new Error('test error'));
KibanaUpgradeStatusApis.getKibanaUpgradeStatus.mockResolvedValue({
totalCriticalDeprecations: 0,
});
await expect(
routeDependencies.router.getHandler({
method: 'get',
pathPattern: '/api/upgrade_assistant/status',
})(routeHandlerContextMock, createRequestMock(), kibanaResponseFactory)
).rejects.toThrow('test error');
});
});
});

View file

@ -0,0 +1,73 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { API_BASE_PATH } from '../../common/constants';
import { getESUpgradeStatus } from '../lib/es_deprecations_status';
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
import { getKibanaUpgradeStatus } from '../lib/kibana_status';
import { RouteDependencies } from '../types';
import { handleEsError } from '../shared_imports';
export function registerUpgradeStatusRoute({ router }: RouteDependencies) {
router.get(
{
path: `${API_BASE_PATH}/status`,
validate: false,
},
versionCheckHandlerWrapper(
async (
{
core: {
elasticsearch: { client: esClient },
deprecations: { client: deprecationsClient },
},
},
request,
response
) => {
try {
// Fetch ES upgrade status
const { totalCriticalDeprecations: esTotalCriticalDeps } = await getESUpgradeStatus(
esClient
);
// Fetch Kibana upgrade status
const {
totalCriticalDeprecations: kibanaTotalCriticalDeps,
} = await getKibanaUpgradeStatus(deprecationsClient);
const readyForUpgrade = esTotalCriticalDeps === 0 && kibanaTotalCriticalDeps === 0;
const getStatusMessage = () => {
if (readyForUpgrade) {
return i18n.translate(
'xpack.upgradeAssistant.status.allDeprecationsResolvedMessage',
{
defaultMessage: 'All deprecation issues have been resolved.',
}
);
}
return i18n.translate('xpack.upgradeAssistant.status.deprecationsUnresolvedMessage', {
defaultMessage:
'You have {esTotalCriticalDeps} Elasticsearch deprecation issues and {kibanaTotalCriticalDeps} Kibana deprecation issues that must be resolved before upgrading.',
values: { esTotalCriticalDeps, kibanaTotalCriticalDeps },
});
};
return response.ok({
body: {
readyForUpgrade,
details: getStatusMessage(),
},
});
} catch (e) {
return handleEsError({ error: e, response });
}
}
)
);
}

View file

@ -35,7 +35,7 @@ export default function ({ getService }: FtrProviderContext) {
});
});
describe('Update index settings route', () => {
describe('POST /api/upgrade_assistant/{indexName}/index_settings', () => {
const indexName = 'update_settings_test_index';
const indexSettings = {
number_of_shards: '3',
@ -121,5 +121,22 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.error).to.eql('Internal Server Error');
});
});
describe('GET /api/upgrade_assistant/status', () => {
it('returns a successful response', async () => {
const { body } = await supertest
.get('/api/upgrade_assistant/status')
.set('kbn-xsrf', 'xxx')
.expect(200);
const expectedResponseKeys = ['readyForUpgrade', 'details'];
// We're not able to easily test different upgrade status scenarios (there are tests with mocked data to handle this)
// so, for now, we simply verify the response returns the expected format
expectedResponseKeys.forEach((key) => {
expect(body[key]).to.not.equal(undefined);
});
});
});
});
}