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

[Fleet] Move Fleet Setup to start lifecycle #117552

Merged
merged 31 commits into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3f504b9
Call setup on fleet start, remove API calls
kpollich Nov 4, 2021
ff41a00
Fix unused import
kpollich Nov 4, 2021
7f5330c
Revert removal of setup API call
kpollich Nov 4, 2021
b377871
Merge branch 'main' into 111858-fleet-setup-on-boot
kpollich Nov 5, 2021
539e6a3
Merge branch 'main' into 111858-fleet-setup-on-boot
kpollich Nov 8, 2021
ee899c3
Merge branch 'main' into 111858-fleet-setup-on-boot
kibanamachine Nov 8, 2021
28c6fbf
Merge branch 'main' into 111858-fleet-setup-on-boot
kibanamachine Nov 8, 2021
252cf54
Restructor fleetSetupCompleted promise
kpollich Nov 8, 2021
b5d900b
Add logging + handle setup failures
kpollich Nov 9, 2021
6c2f05e
Merge branch 'main' into 111858-fleet-setup-on-boot
kibanamachine Nov 9, 2021
1fc6f01
Restructure logging to mix of debug/info
kpollich Nov 9, 2021
06afb9e
Merge branch 'main' into 111858-fleet-setup-on-boot
kpollich Nov 9, 2021
c850ae8
Merge branch 'main' into 111858-fleet-setup-on-boot
kibanamachine Nov 9, 2021
23c4ac1
Maybe fix failing tests
kpollich Nov 9, 2021
8fe61e2
Try fixing tests again
kpollich Nov 10, 2021
e135fdd
Fix another dashboard test
kpollich Nov 10, 2021
6311892
Merge branch 'main' into 111858-fleet-setup-on-boot
kpollich Nov 10, 2021
4c2e113
Re-add output logs after merge
kpollich Nov 10, 2021
0682d83
Merge branch 'main' into 111858-fleet-setup-on-boot
kibanamachine Nov 10, 2021
162dbd6
Merge branch 'main' into 111858-fleet-setup-on-boot
kpollich Nov 11, 2021
3c019ba
Log non-fatal errors during Fleet setup on boot
kpollich Nov 11, 2021
8e2e1be
Don't rely on fleetSetupCompleted to be called
kpollich Nov 11, 2021
ede4a18
Fix failing test
kpollich Nov 11, 2021
95475d4
Track fleet setup status to avoid double calls
kpollich Nov 11, 2021
f1f9fc6
Merge branch 'main' into 111858-fleet-setup-on-boot
kibanamachine Nov 11, 2021
c937d62
Use IIFE in place of Promise ctor
kpollich Nov 11, 2021
76e4829
Remove unnecessary fleetSetupStatus value
kpollich Nov 11, 2021
1c4a465
Merge branch 'main' into 111858-fleet-setup-on-boot
kpollich Nov 15, 2021
76138ec
Move non-error logs into setupFleet method
kpollich Nov 15, 2021
59190ba
Remove unused formatNonFatalErrors import
kpollich Nov 15, 2021
697f8be
Merge branch 'main' into 111858-fleet-setup-on-boot
kibanamachine Nov 15, 2021
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();
joshdover marked this conversation as resolved.
Show resolved Hide resolved

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,
};
});
}
});
}