Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.0] [Fleet] Move Fleet Setup to start lifecycle (#117552) #118554

Merged
merged 1 commit into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions test/accessibility/apps/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});

it('add a visualization', async () => {
await testSubjects.setValue('savedObjectFinderSearchInput', '[Flights]');
await testSubjects.click('savedObjectTitle[Flights]-Delay-Buckets');
await a11y.testAppSnapshot();
});
Expand Down Expand Up @@ -85,6 +86,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});

it('Add one more saved object to cancel it', async () => {
await testSubjects.setValue('savedObjectFinderSearchInput', '[Flights]');
await testSubjects.click('savedObjectTitle[Flights]-Destination-Weather');
await a11y.testAppSnapshot();
});
Expand Down
2 changes: 2 additions & 0 deletions test/examples/embeddables/adding_children.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
await testSubjects.click('embeddablePanelToggleMenuIcon');
await testSubjects.click('embeddablePanelAction-ACTION_ADD_PANEL');
await testSubjects.waitForDeleted('savedObjectFinderLoadingIndicator');
await testSubjects.click('savedObjectFinderFilterButton');
await testSubjects.click('savedObjectFinderFilter-todo');
await testSubjects.click('savedObjectTitleGarbage');
await testSubjects.moveMouseTo('euiFlyoutCloseButton');
await flyout.ensureClosed('dashboardAddPanel');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.savedObjects.clickConfirmChanges();
await PageObjects.savedObjects.clickImportDone();
await PageObjects.savedObjects.waitTableIsLoaded();
await PageObjects.savedObjects.searchForObject('mysaved');

//instead of asserting on count- am asserting on the titles- which is more accurate than count.
const objects = await PageObjects.savedObjects.getRowTitles();
Expand Down
19 changes: 17 additions & 2 deletions x-pack/plugins/fleet/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server';

import type { TelemetryPluginSetup, TelemetryPluginStart } from 'src/plugins/telemetry/server';

import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
import { DEFAULT_APP_CATEGORIES, SavedObjectsClient } from '../../../../src/core/server';
import type { PluginStart as DataPluginStart } from '../../../../src/plugins/data/server';
import type { LicensingPluginSetup, ILicense } from '../../licensing/server';
import type {
Expand Down Expand Up @@ -83,6 +83,7 @@ import { RouterWrappers } from './routes/security';
import { FleetArtifactsClient } from './services/artifacts';
import type { FleetRouter } from './types/request_context';
import { TelemetryEventsSender } from './telemetry/sender';
import { setupFleet } from './services/setup';

export interface FleetSetupDeps {
licensing: LicensingPluginSetup;
Expand Down Expand Up @@ -332,8 +333,22 @@ export class FleetPlugin

this.telemetryEventsSender.start(plugins.telemetry, core);

const logger = appContextService.getLogger();

const fleetSetupPromise = (async () => {
try {
await setupFleet(
new SavedObjectsClient(core.savedObjects.createInternalRepository()),
core.elasticsearch.client.asInternalUser
);
} catch (error) {
logger.warn('Fleet setup failed');
logger.warn(error);
}
})();

return {
fleetSetupCompleted: () => Promise.resolve(),
fleetSetupCompleted: () => fleetSetupPromise,
esIndexPatternService: new ESIndexPatternSavedObjectService(),
packageService: {
getInstallation,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/routes/setup/handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { fleetSetupHandler } from './handlers';

jest.mock('../../services/setup', () => {
return {
...jest.requireActual('../../services/setup'),
setupFleet: jest.fn(),
};
});
Expand Down
20 changes: 2 additions & 18 deletions x-pack/plugins/fleet/server/routes/setup/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { appContextService } from '../../services';
import type { GetFleetStatusResponse, PostFleetSetupResponse } from '../../../common';
import { setupFleet } from '../../services/setup';
import { formatNonFatalErrors, setupFleet } from '../../services/setup';
import { hasFleetServers } from '../../services/fleet_server';
import { defaultIngestErrorHandler } from '../../errors';
import type { FleetRequestHandler } from '../../types';
Expand Down Expand Up @@ -50,24 +50,8 @@ export const fleetSetupHandler: FleetRequestHandler = async (context, request, r
const setupStatus = await setupFleet(soClient, esClient);
const body: PostFleetSetupResponse = {
...setupStatus,
nonFatalErrors: setupStatus.nonFatalErrors.flatMap((e) => {
// JSONify the error object so it can be displayed properly in the UI
if ('error' in e) {
return {
name: e.error.name,
message: e.error.message,
};
} else {
return e.errors.map((upgradePackagePolicyError: any) => {
return {
name: upgradePackagePolicyError.key,
message: upgradePackagePolicyError.message,
};
});
}
}),
nonFatalErrors: formatNonFatalErrors(setupStatus.nonFatalErrors),
};

return response.ok({ body });
} catch (error) {
return defaultIngestErrorHandler({ error, response });
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/fleet/server/services/preconfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export async function ensurePreconfiguredOutputs(
esClient: ElasticsearchClient,
outputs: PreconfiguredOutput[]
) {
const logger = appContextService.getLogger();

if (outputs.length === 0) {
return;
}
Expand Down Expand Up @@ -106,8 +108,10 @@ export async function ensurePreconfiguredOutputs(
existingOutput && isPreconfiguredOutputDifferentFromCurrent(existingOutput, data);

if (isCreate) {
logger.debug(`Creating output ${output.id}`);
await outputService.create(soClient, data, { id, fromPreconfiguration: true });
} else if (isUpdateWithNewData) {
logger.debug(`Updating output ${output.id}`);
await outputService.update(soClient, id, data, { fromPreconfiguration: true });
// Bump revision of all policies using that output
if (outputData.is_default || outputData.is_default_monitoring) {
Expand Down Expand Up @@ -335,7 +339,7 @@ export async function ensurePreconfiguredPackagesAndPolicies(
await soClient
.delete(AGENT_POLICY_SAVED_OBJECT_TYPE, policy!.id)
// swallow error
.catch((deleteErr) => appContextService.getLogger().error(deleteErr));
.catch((deleteErr) => logger.error(deleteErr));

throw err;
}
Expand Down
45 changes: 44 additions & 1 deletion x-pack/plugins/fleet/server/services/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ async function createSetupSideEffects(
soClient: SavedObjectsClientContract,
esClient: ElasticsearchClient
): Promise<SetupStatus> {
const logger = appContextService.getLogger();
logger.info('Beginning fleet setup');

const {
agentPolicies: policiesOrUndefined,
packages: packagesOrUndefined,
Expand All @@ -60,6 +63,7 @@ async function createSetupSideEffects(
const policies = policiesOrUndefined ?? [];
let packages = packagesOrUndefined ?? [];

logger.debug('Setting up Fleet outputs');
await Promise.all([
ensurePreconfiguredOutputs(soClient, esClient, outputsOrUndefined ?? []),
settingsService.settingsSetup(soClient),
Expand All @@ -68,6 +72,7 @@ async function createSetupSideEffects(
const defaultOutput = await outputService.ensureDefaultOutput(soClient);

if (appContextService.getConfig()?.agentIdVerificationEnabled) {
logger.debug('Setting up Fleet Elasticsearch assets');
await ensureFleetGlobalEsAssets(soClient, esClient);
}

Expand All @@ -91,6 +96,8 @@ async function createSetupSideEffects(
...autoUpdateablePackages.filter((pkg) => !preconfiguredPackageNames.has(pkg.name)),
];

logger.debug('Setting up initial Fleet packages');

const { nonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies(
soClient,
esClient,
Expand All @@ -99,11 +106,22 @@ async function createSetupSideEffects(
defaultOutput
);

logger.debug('Cleaning up Fleet outputs');
await cleanPreconfiguredOutputs(soClient, outputsOrUndefined ?? []);

logger.debug('Setting up Fleet enrollment keys');
await ensureDefaultEnrollmentAPIKeysExists(soClient, esClient);

logger.debug('Setting up Fleet Server agent policies');
await ensureFleetServerAgentPoliciesExists(soClient, esClient);

if (nonFatalErrors.length > 0) {
logger.info('Encountered non fatal errors during Fleet setup');
formatNonFatalErrors(nonFatalErrors).forEach((error) => logger.info(JSON.stringify(error)));
}

logger.info('Fleet setup completed');

return {
isInitialized: true,
nonFatalErrors,
Expand All @@ -119,6 +137,7 @@ export async function ensureFleetGlobalEsAssets(
) {
const logger = appContextService.getLogger();
// Ensure Global Fleet ES assets are installed
logger.debug('Creating Fleet component template and ingest pipeline');
const globalAssetsRes = await Promise.all([
ensureDefaultComponentTemplate(esClient),
ensureFleetFinalPipelineIsInstalled(esClient),
Expand All @@ -141,7 +160,7 @@ export async function ensureFleetGlobalEsAssets(
savedObjectsClient: soClient,
pkgkey: pkgToPkgKey({ name: installation.name, version: installation.version }),
esClient,
// Force install the pacakge will update the index template and the datastream write indices
// Force install the package will update the index template and the datastream write indices
force: true,
}).catch((err) => {
logger.error(
Expand Down Expand Up @@ -187,3 +206,27 @@ export async function ensureDefaultEnrollmentAPIKeysExists(
})
);
}

/**
* Maps the `nonFatalErrors` object returned by the setup process to a more readable
* and predictable format suitable for logging output or UI presentation.
*/
export function formatNonFatalErrors(
nonFatalErrors: SetupStatus['nonFatalErrors']
): Array<{ name: string; message: string }> {
return nonFatalErrors.flatMap((e) => {
if ('error' in e) {
return {
name: e.error.name,
message: e.error.message,
};
} else {
return e.errors.map((upgradePackagePolicyError: any) => {
return {
name: upgradePackagePolicyError.key,
message: upgradePackagePolicyError.message,
};
});
}
});
}