-
Notifications
You must be signed in to change notification settings - Fork 8.2k
/
default_space_service.ts
135 lines (116 loc) · 4.15 KB
/
default_space_service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
* 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 type { Observable, ObservableInput, Subscription } from 'rxjs';
import {
BehaviorSubject,
catchError,
combineLatest,
concat,
defer,
mergeMap,
of,
switchMap,
tap,
timer,
} from 'rxjs';
import type { OnBoardingDefaultSolution } from '@kbn/cloud-plugin/common';
import type { CoreSetup, Logger, SavedObjectsServiceStart, ServiceStatus } from '@kbn/core/server';
import { ServiceStatusLevels } from '@kbn/core/server';
import type { ILicense } from '@kbn/licensing-plugin/server';
import { createDefaultSpace } from './create_default_space';
import type { SpacesLicense } from '../../common/licensing';
interface Deps {
coreStatus: CoreSetup['status'];
getSavedObjects: () => Promise<Pick<SavedObjectsServiceStart, 'createInternalRepository'>>;
license$: Observable<ILicense>;
spacesLicense: SpacesLicense;
logger: Logger;
solution?: OnBoardingDefaultSolution;
}
export const RETRY_SCALE_DURATION = 100;
export const RETRY_DURATION_MAX = 10000;
const calculateDuration = (i: number) => {
const duration = i * RETRY_SCALE_DURATION;
if (duration > RETRY_DURATION_MAX) {
return RETRY_DURATION_MAX;
}
return duration;
};
// we can't use a retryWhen here, because we want to propagate the unavailable status and then retry
const propagateUnavailableStatusAndScaleRetry = () => {
let i = 0;
return (err: Error, caught: ObservableInput<any>) =>
concat(
of({
level: ServiceStatusLevels.unavailable,
summary: `Error creating default space: ${err.message}`,
}),
timer(calculateDuration(++i)).pipe(mergeMap(() => caught))
);
};
export class DefaultSpaceService {
private initializeSubscription?: Subscription;
private serviceStatus$?: BehaviorSubject<ServiceStatus>;
public setup({ coreStatus, getSavedObjects, license$, spacesLicense, logger, solution }: Deps) {
const statusLogger = logger.get('status');
this.serviceStatus$ = new BehaviorSubject({
level: ServiceStatusLevels.unavailable,
summary: 'not initialized',
} as ServiceStatus);
this.initializeSubscription = combineLatest([coreStatus.core$, license$])
.pipe(
switchMap(([status]) => {
const isElasticsearchReady = status.elasticsearch.level === ServiceStatusLevels.available;
const isSavedObjectsReady = status.savedObjects.level === ServiceStatusLevels.available;
if (!isElasticsearchReady || !isSavedObjectsReady) {
return of({
level: ServiceStatusLevels.unavailable,
summary: 'required core services are not ready',
} as ServiceStatus);
}
if (!spacesLicense.isEnabled()) {
return of({
level: ServiceStatusLevels.unavailable,
summary: 'missing or invalid license',
} as ServiceStatus);
}
return defer(() =>
createDefaultSpace({
getSavedObjects,
logger,
solution,
}).then(() => {
return {
level: ServiceStatusLevels.available,
summary: 'ready',
};
})
).pipe(catchError(propagateUnavailableStatusAndScaleRetry()));
}),
tap<ServiceStatus>((spacesStatus) => {
// This is temporary for debugging/visibility until we get a proper status service from core.
// See issue #41983 for details.
statusLogger.debug(`${spacesStatus.level.toString()}: ${spacesStatus.summary}`);
this.serviceStatus$!.next(spacesStatus);
})
)
.subscribe();
return {
serviceStatus$: this.serviceStatus$!.asObservable(),
};
}
public stop() {
if (this.initializeSubscription) {
this.initializeSubscription.unsubscribe();
}
this.initializeSubscription = undefined;
if (this.serviceStatus$) {
this.serviceStatus$.complete();
this.serviceStatus$ = undefined;
}
}
}