kibana/x-pack/plugins/fleet/server/plugin.ts
Josh Dover 8052f03f61
[Fleet] Cap setup attempts to 50 on Serverless (#171550)
## Summary

If there is a bug in Fleet setup, we can retrigger rollovers on each
attempt, causing shard explosion in Elasticsearch. We need a sane limit
to prevent this from happening, which this PR introduces.

The impact of Fleet setup failing to complete (Agents may not be able to
be enrolled) is much smaller than causing shard explosion, so this seems
like an acceptable tradeoff.

This is only a small part of the overall solution - in the current
incident we have, it's still unclear why we are failing to rollover the
index and getting into this loop.

### Checklist

Delete any items that are not applicable to this PR.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [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
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
2023-11-20 18:47:57 +01:00

701 lines
24 KiB
TypeScript

/*
* 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 { backOff } from 'exponential-backoff';
import type { Observable } from 'rxjs';
import { BehaviorSubject } from 'rxjs';
import { take, filter } from 'rxjs/operators';
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
import { i18n } from '@kbn/i18n';
import type {
CoreSetup,
CoreStart,
ElasticsearchServiceStart,
Logger,
Plugin,
PluginInitializerContext,
SavedObjectsServiceStart,
HttpServiceSetup,
KibanaRequest,
ServiceStatus,
ElasticsearchClient,
SavedObjectsClientContract,
} from '@kbn/core/server';
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
import type { TelemetryPluginSetup, TelemetryPluginStart } from '@kbn/telemetry-plugin/server';
import { DEFAULT_APP_CATEGORIES, SavedObjectsClient, ServiceStatusLevels } from '@kbn/core/server';
import type { PluginStart as DataPluginStart } from '@kbn/data-plugin/server';
import type { LicensingPluginStart } from '@kbn/licensing-plugin/server';
import type {
EncryptedSavedObjectsPluginStart,
EncryptedSavedObjectsPluginSetup,
} from '@kbn/encrypted-saved-objects-plugin/server';
import type {
AuditLogger,
SecurityPluginSetup,
SecurityPluginStart,
} from '@kbn/security-plugin/server';
import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
import type {
TaskManagerSetupContract,
TaskManagerStartContract,
} from '@kbn/task-manager-plugin/server';
import type { CloudSetup } from '@kbn/cloud-plugin/server';
import type { SpacesPluginStart } from '@kbn/spaces-plugin/server';
import type { SavedObjectTaggingStart } from '@kbn/saved-objects-tagging-plugin/server';
import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server';
import type { FleetConfigType } from '../common/types';
import type { FleetAuthz } from '../common';
import type { ExperimentalFeatures } from '../common/experimental_features';
import {
MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE,
INTEGRATIONS_PLUGIN_ID,
UNINSTALL_TOKENS_SAVED_OBJECT_TYPE,
} from '../common';
import { parseExperimentalConfigValue } from '../common/experimental_features';
import { getFilesClientFactory } from './services/files/get_files_client_factory';
import type { MessageSigningServiceInterface } from './services/security';
import {
getRouteRequiredAuthz,
makeRouterWithFleetAuthz,
calculateRouteAuthz,
getAuthzFromRequest,
MessageSigningService,
} from './services/security';
import {
PLUGIN_ID,
OUTPUT_SAVED_OBJECT_TYPE,
AGENT_POLICY_SAVED_OBJECT_TYPE,
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
PACKAGES_SAVED_OBJECT_TYPE,
ASSETS_SAVED_OBJECT_TYPE,
PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE,
DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
FLEET_SERVER_HOST_SAVED_OBJECT_TYPE,
} from './constants';
import { registerSavedObjects, registerEncryptedSavedObjects } from './saved_objects';
import { registerRoutes } from './routes';
import type { ExternalCallback, FleetRequestHandlerContext } from './types';
import type {
ESIndexPatternService,
AgentService,
AgentPolicyServiceInterface,
PackageService,
} from './services';
import { FleetUsageSender } from './services';
import {
appContextService,
licenseService,
ESIndexPatternSavedObjectService,
agentPolicyService,
packagePolicyService,
AgentServiceImpl,
PackageServiceImpl,
} from './services';
import {
registerFleetUsageCollector,
fetchAgentsUsage,
fetchFleetUsage,
} from './collectors/register';
import { FleetArtifactsClient } from './services/artifacts';
import type { FleetRouter } from './types/request_context';
import { TelemetryEventsSender } from './telemetry/sender';
import { setupFleet } from './services/setup';
import { BulkActionsResolver } from './services/agents';
import type { PackagePolicyService } from './services/package_policy_service';
import { PackagePolicyServiceImpl } from './services/package_policy';
import { registerFleetUsageLogger, startFleetUsageLogger } from './services/fleet_usage_logger';
import { CheckDeletedFilesTask } from './tasks/check_deleted_files_task';
import {
UninstallTokenService,
type UninstallTokenServiceInterface,
} from './services/security/uninstall_token_service';
import { FleetActionsClient, type FleetActionsClientInterface } from './services/actions';
import type { FilesClientFactory } from './services/files/types';
import { PolicyWatcher } from './services/agent_policy_watch';
import { getPackageSpecTagId } from './services/epm/kibana/assets/tag_assets';
import { FleetMetricsTask } from './services/metrics/fleet_metrics_task';
import { fetchAgentMetrics } from './services/metrics/fetch_agent_metrics';
export interface FleetSetupDeps {
security: SecurityPluginSetup;
features?: FeaturesPluginSetup;
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup;
cloud?: CloudSetup;
usageCollection?: UsageCollectionSetup;
spaces?: SpacesPluginStart;
telemetry?: TelemetryPluginSetup;
taskManager: TaskManagerSetupContract;
}
export interface FleetStartDeps {
data: DataPluginStart;
licensing: LicensingPluginStart;
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
security: SecurityPluginStart;
telemetry?: TelemetryPluginStart;
savedObjectsTagging: SavedObjectTaggingStart;
taskManager: TaskManagerStartContract;
}
export interface FleetAppContext {
elasticsearch: ElasticsearchServiceStart;
data: DataPluginStart;
encryptedSavedObjectsStart?: EncryptedSavedObjectsPluginStart;
encryptedSavedObjectsSetup?: EncryptedSavedObjectsPluginSetup;
securitySetup: SecurityPluginSetup;
securityStart: SecurityPluginStart;
config$?: Observable<FleetConfigType>;
configInitialValue: FleetConfigType;
experimentalFeatures: ExperimentalFeatures;
savedObjects: SavedObjectsServiceStart;
savedObjectsTagging?: SavedObjectTaggingStart;
isProductionMode: PluginInitializerContext['env']['mode']['prod'];
kibanaVersion: PluginInitializerContext['env']['packageInfo']['version'];
kibanaBranch: PluginInitializerContext['env']['packageInfo']['branch'];
kibanaInstanceId: PluginInitializerContext['env']['instanceUuid'];
cloud?: CloudSetup;
logger?: Logger;
httpSetup?: HttpServiceSetup;
telemetryEventsSender: TelemetryEventsSender;
bulkActionsResolver: BulkActionsResolver;
messageSigningService: MessageSigningServiceInterface;
auditLogger?: AuditLogger;
uninstallTokenService: UninstallTokenServiceInterface;
}
export type FleetSetupContract = void;
const allSavedObjectTypes = [
OUTPUT_SAVED_OBJECT_TYPE,
AGENT_POLICY_SAVED_OBJECT_TYPE,
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
PACKAGES_SAVED_OBJECT_TYPE,
ASSETS_SAVED_OBJECT_TYPE,
PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE,
DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
FLEET_SERVER_HOST_SAVED_OBJECT_TYPE,
];
/**
* Describes public Fleet plugin contract returned at the `startup` stage.
*/
export interface FleetStartContract {
/**
* returns a promise that resolved when fleet setup has been completed regardless if it was successful or failed).
* Any consumer of fleet start services should first `await` for this promise to be resolved before using those
* services
*/
fleetSetupCompleted: () => Promise<void>;
authz: {
fromRequest(request: KibanaRequest): Promise<FleetAuthz>;
};
esIndexPatternService: ESIndexPatternService;
packageService: PackageService;
agentService: AgentService;
/**
* Services for Fleet's package policies
*/
packagePolicyService: typeof packagePolicyService;
agentPolicyService: AgentPolicyServiceInterface;
/**
* Register callbacks for inclusion in fleet API processing
* @param args
*/
registerExternalCallback: (...args: ExternalCallback) => void;
/**
* Create a Fleet Artifact Client instance
* @param packageName
*/
createArtifactsClient: (packageName: string) => FleetArtifactsClient;
/**
* Create a Fleet Files client instance
* @param packageName
* @param type
* @param maxSizeBytes
*/
createFilesClient: Readonly<FilesClientFactory>;
messageSigningService: MessageSigningServiceInterface;
uninstallTokenService: UninstallTokenServiceInterface;
createFleetActionsClient: (packageName: string) => FleetActionsClientInterface;
/*
Function exported to allow creating unique ids for saved object tags
*/
getPackageSpecTagId: (spaceId: string, pkgName: string, tagName: string) => string;
}
export class FleetPlugin
implements Plugin<FleetSetupContract, FleetStartContract, FleetSetupDeps, FleetStartDeps>
{
private config$: Observable<FleetConfigType>;
private configInitialValue: FleetConfigType;
private cloud?: CloudSetup;
private logger?: Logger;
private isProductionMode: FleetAppContext['isProductionMode'];
private kibanaVersion: FleetAppContext['kibanaVersion'];
private kibanaBranch: FleetAppContext['kibanaBranch'];
private kibanaInstanceId: FleetAppContext['kibanaInstanceId'];
private httpSetup?: HttpServiceSetup;
private securitySetup!: SecurityPluginSetup;
private encryptedSavedObjectsSetup?: EncryptedSavedObjectsPluginSetup;
private readonly telemetryEventsSender: TelemetryEventsSender;
private readonly fleetStatus$: BehaviorSubject<ServiceStatus>;
private bulkActionsResolver?: BulkActionsResolver;
private fleetUsageSender?: FleetUsageSender;
private checkDeletedFilesTask?: CheckDeletedFilesTask;
private fleetMetricsTask?: FleetMetricsTask;
private agentService?: AgentService;
private packageService?: PackageService;
private packagePolicyService?: PackagePolicyService;
private policyWatcher?: PolicyWatcher;
constructor(private readonly initializerContext: PluginInitializerContext) {
this.config$ = this.initializerContext.config.create<FleetConfigType>();
this.isProductionMode = this.initializerContext.env.mode.prod;
this.kibanaVersion = this.initializerContext.env.packageInfo.version;
this.kibanaBranch = this.initializerContext.env.packageInfo.branch;
this.kibanaInstanceId = this.initializerContext.env.instanceUuid;
this.logger = this.initializerContext.logger.get();
this.configInitialValue = this.initializerContext.config.get();
this.telemetryEventsSender = new TelemetryEventsSender(this.logger.get('telemetry_events'));
this.fleetStatus$ = new BehaviorSubject<ServiceStatus>({
level: ServiceStatusLevels.unavailable,
summary: 'Fleet is unavailable',
});
}
public setup(core: CoreSetup, deps: FleetSetupDeps) {
this.httpSetup = core.http;
this.encryptedSavedObjectsSetup = deps.encryptedSavedObjects;
this.cloud = deps.cloud;
this.securitySetup = deps.security;
const config = this.configInitialValue;
core.status.set(this.fleetStatus$.asObservable());
registerSavedObjects(core.savedObjects);
registerEncryptedSavedObjects(deps.encryptedSavedObjects);
// Register feature
if (deps.features) {
deps.features.registerKibanaFeature({
id: `fleetv2`,
name: 'Fleet',
category: DEFAULT_APP_CATEGORIES.management,
app: [PLUGIN_ID],
catalogue: ['fleet'],
privilegesTooltip: i18n.translate('xpack.fleet.serverPlugin.privilegesTooltip', {
defaultMessage: 'All Spaces is required for Fleet access.',
}),
reserved: {
description:
'Privilege to setup Fleet packages and configured policies. Intended for use by the elastic/fleet-server service account only.',
privileges: [
{
id: 'fleet-setup',
privilege: {
excludeFromBasePrivileges: true,
api: ['fleet-setup'],
savedObject: {
all: [],
read: [],
},
ui: [],
},
},
],
},
privileges: {
all: {
api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-all`],
app: [PLUGIN_ID],
requireAllSpaces: true,
catalogue: ['fleet'],
savedObject: {
all: allSavedObjectTypes,
read: [],
},
ui: ['read', 'all'],
},
read: {
api: [`${PLUGIN_ID}-read`],
app: [PLUGIN_ID],
catalogue: ['fleet'],
requireAllSpaces: true,
savedObject: {
all: [],
read: allSavedObjectTypes,
},
ui: ['read'],
disabled: true,
},
},
});
deps.features.registerKibanaFeature({
id: 'fleet', // for BWC
name: 'Integrations',
category: DEFAULT_APP_CATEGORIES.management,
app: [INTEGRATIONS_PLUGIN_ID],
catalogue: ['fleet'],
privileges: {
all: {
api: [`${INTEGRATIONS_PLUGIN_ID}-read`, `${INTEGRATIONS_PLUGIN_ID}-all`],
app: [INTEGRATIONS_PLUGIN_ID],
catalogue: ['fleet'],
savedObject: {
all: allSavedObjectTypes,
read: [],
},
ui: ['read', 'all'],
},
read: {
api: [`${INTEGRATIONS_PLUGIN_ID}-read`],
app: [INTEGRATIONS_PLUGIN_ID],
catalogue: ['fleet'],
savedObject: {
all: [],
read: allSavedObjectTypes,
},
ui: ['read'],
},
},
subFeatures: [],
});
}
core.http.registerRouteHandlerContext<FleetRequestHandlerContext, typeof PLUGIN_ID>(
PLUGIN_ID,
async (context, request) => {
const plugin = this;
const coreContext = await context.core;
const authz = await getAuthzFromRequest(request);
const esClient = coreContext.elasticsearch.client;
const soClient = coreContext.savedObjects.getClient();
const routeRequiredAuthz = getRouteRequiredAuthz(request.route.method, request.route.path);
const routeAuthz = routeRequiredAuthz
? calculateRouteAuthz(authz, routeRequiredAuthz)
: undefined;
const getInternalSoClient = (): SavedObjectsClientContract =>
appContextService
.getSavedObjects()
.getScopedClient(request, { excludedExtensions: [SECURITY_EXTENSION_ID] });
return {
get agentClient() {
const agentService = plugin.setupAgentService(esClient.asInternalUser, soClient);
return {
asCurrentUser: agentService.asScoped(request),
asInternalUser: agentService.asInternalUser,
};
},
get packagePolicyService() {
const service = plugin.setupPackagePolicyService();
return {
asCurrentUser: service.asScoped(request),
asInternalUser: service.asInternalUser,
};
},
authz,
get internalSoClient() {
// Use a lazy getter to avoid constructing this client when not used by a request handler
return getInternalSoClient();
},
get spaceId() {
return deps.spaces?.spacesService?.getSpaceId(request) ?? DEFAULT_SPACE_ID;
},
get limitedToPackages() {
if (routeAuthz && routeAuthz.granted) {
return routeAuthz.scopeDataToPackages;
}
},
};
}
);
// Register usage collection
registerFleetUsageCollector(core, config, deps.usageCollection);
const fetch = async (abortController: AbortController) =>
await fetchFleetUsage(core, config, abortController);
this.fleetUsageSender = new FleetUsageSender(deps.taskManager, core, fetch);
registerFleetUsageLogger(deps.taskManager, async () => fetchAgentsUsage(core, config));
const fetchAgents = async (abortController: AbortController) =>
await fetchAgentMetrics(core, abortController);
this.fleetMetricsTask = new FleetMetricsTask(deps.taskManager, fetchAgents);
const router: FleetRouter = core.http.createRouter<FleetRequestHandlerContext>();
// Allow read-only users access to endpoints necessary for Integrations UI
// Only some endpoints require superuser so we pass a raw IRouter here
// For all the routes we enforce the user to have role superuser
const fleetAuthzRouter = makeRouterWithFleetAuthz(
router,
this.initializerContext.logger.get('fleet_authz_router')
);
registerRoutes(fleetAuthzRouter, config);
this.telemetryEventsSender.setup(deps.telemetry);
this.bulkActionsResolver = new BulkActionsResolver(deps.taskManager, core);
this.checkDeletedFilesTask = new CheckDeletedFilesTask({
core,
taskManager: deps.taskManager,
logFactory: this.initializerContext.logger,
});
}
public start(core: CoreStart, plugins: FleetStartDeps): FleetStartContract {
const messageSigningService = new MessageSigningService(
this.initializerContext.logger,
plugins.encryptedSavedObjects.getClient({
includedHiddenTypes: [MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE],
})
);
const uninstallTokenService = new UninstallTokenService(
plugins.encryptedSavedObjects.getClient({
includedHiddenTypes: [UNINSTALL_TOKENS_SAVED_OBJECT_TYPE],
})
);
appContextService.start({
elasticsearch: core.elasticsearch,
data: plugins.data,
encryptedSavedObjectsStart: plugins.encryptedSavedObjects,
encryptedSavedObjectsSetup: this.encryptedSavedObjectsSetup,
securitySetup: this.securitySetup,
securityStart: plugins.security,
configInitialValue: this.configInitialValue,
config$: this.config$,
experimentalFeatures: parseExperimentalConfigValue(
this.configInitialValue.enableExperimental || []
),
savedObjects: core.savedObjects,
savedObjectsTagging: plugins.savedObjectsTagging,
isProductionMode: this.isProductionMode,
kibanaVersion: this.kibanaVersion,
kibanaBranch: this.kibanaBranch,
kibanaInstanceId: this.kibanaInstanceId,
httpSetup: this.httpSetup,
cloud: this.cloud,
logger: this.logger,
telemetryEventsSender: this.telemetryEventsSender,
bulkActionsResolver: this.bulkActionsResolver!,
messageSigningService,
uninstallTokenService,
});
licenseService.start(plugins.licensing.license$);
this.telemetryEventsSender.start(plugins.telemetry, core);
this.bulkActionsResolver?.start(plugins.taskManager);
this.fleetUsageSender?.start(plugins.taskManager);
this.checkDeletedFilesTask?.start({ taskManager: plugins.taskManager });
startFleetUsageLogger(plugins.taskManager);
this.fleetMetricsTask?.start(plugins.taskManager, core.elasticsearch.client.asInternalUser);
const logger = appContextService.getLogger();
this.policyWatcher = new PolicyWatcher(core.savedObjects, core.elasticsearch, logger);
this.policyWatcher.start(licenseService);
// We only retry when this feature flag is enabled (Serverless)
const setupAttempts = this.configInitialValue.internal?.retrySetupOnBoot ? 25 : 1;
const fleetSetupPromise = (async () => {
try {
// Fleet remains `available` during setup as to excessively delay Kibana's boot process.
// This should be reevaluated as Fleet's setup process is optimized and stabilized.
this.fleetStatus$.next({
level: ServiceStatusLevels.available,
summary: 'Fleet is setting up',
});
// We need to wait for the licence feature to be available,
// to have our internal saved object client with encrypted saved object working properly
await plugins.licensing.license$
.pipe(
filter(
(licence) =>
licence.getFeature('security').isEnabled &&
licence.getFeature('security').isAvailable
),
take(1)
)
.toPromise();
// Retry Fleet setup w/ backoff
await backOff(
async () => {
await setupFleet(
new SavedObjectsClient(core.savedObjects.createInternalRepository()),
core.elasticsearch.client.asInternalUser
);
},
{
numOfAttempts: setupAttempts,
// 1s initial backoff
startingDelay: 1000,
// 5m max backoff
maxDelay: 60000 * 5,
timeMultiple: 2,
// avoid HA contention with other Kibana instances
jitter: 'full',
retry: (error: any, attemptCount: number) => {
const summary = `Fleet setup attempt ${attemptCount} failed, will retry after backoff`;
logger.warn(summary, { error: { message: error } });
this.fleetStatus$.next({
level: ServiceStatusLevels.available,
summary,
meta: {
attemptCount,
error,
},
});
return true;
},
}
);
this.fleetStatus$.next({
level: ServiceStatusLevels.available,
summary: 'Fleet is available',
});
} catch (error) {
logger.warn(`Fleet setup failed after ${setupAttempts} attempts`, {
error: { message: error },
});
this.fleetStatus$.next({
// As long as Fleet has a dependency on EPR, we can't reliably set Kibana status to `unavailable` here.
// See https://github.com/elastic/kibana/issues/120237
level: ServiceStatusLevels.available,
summary: 'Fleet setup failed',
meta: {
error: error.message,
},
});
}
})();
const internalSoClient = new SavedObjectsClient(core.savedObjects.createInternalRepository());
return {
authz: {
fromRequest: getAuthzFromRequest,
},
fleetSetupCompleted: () => fleetSetupPromise,
esIndexPatternService: new ESIndexPatternSavedObjectService(),
packageService: this.setupPackageService(
core.elasticsearch.client.asInternalUser,
internalSoClient
),
agentService: this.setupAgentService(
core.elasticsearch.client.asInternalUser,
internalSoClient
),
agentPolicyService: {
get: agentPolicyService.get,
list: agentPolicyService.list,
getFullAgentPolicy: agentPolicyService.getFullAgentPolicy,
getByIds: agentPolicyService.getByIDs,
},
packagePolicyService,
registerExternalCallback: (type: ExternalCallback[0], callback: ExternalCallback[1]) => {
return appContextService.addExternalCallback(type, callback);
},
createArtifactsClient(packageName: string) {
return new FleetArtifactsClient(core.elasticsearch.client.asInternalUser, packageName);
},
createFilesClient: Object.freeze(
getFilesClientFactory({
esClient: core.elasticsearch.client.asInternalUser,
logger: this.initializerContext.logger,
})
),
messageSigningService,
uninstallTokenService,
createFleetActionsClient(packageName: string) {
return new FleetActionsClient(core.elasticsearch.client.asInternalUser, packageName);
},
getPackageSpecTagId,
};
}
public stop() {
appContextService.stop();
this.policyWatcher?.stop();
licenseService.stop();
this.telemetryEventsSender.stop();
this.fleetStatus$.complete();
}
private setupAgentService(
internalEsClient: ElasticsearchClient,
internalSoClient: SavedObjectsClientContract
): AgentService {
if (this.agentService) {
return this.agentService;
}
this.agentService = new AgentServiceImpl(internalEsClient, internalSoClient);
return this.agentService;
}
private setupPackagePolicyService(): PackagePolicyService {
if (this.packagePolicyService) {
return this.packagePolicyService;
}
this.packagePolicyService = new PackagePolicyServiceImpl();
return this.packagePolicyService;
}
private setupPackageService(
internalEsClient: ElasticsearchClient,
internalSoClient: SavedObjectsClientContract
): PackageService {
if (this.packageService) {
return this.packageService;
}
this.packageService = new PackageServiceImpl(
internalEsClient,
internalSoClient,
this.getLogger()
);
return this.packageService!;
}
private getLogger(): Logger {
if (!this.logger) {
this.logger = this.initializerContext.logger.get();
}
return this.logger;
}
}