Skip to content
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
4 changes: 4 additions & 0 deletions .buildkite/scripts/packer_cache.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ for version in $(cat versions.json | jq -r '.versions[].version'); do
node scripts/es snapshot --download-only --base-path "$ES_CACHE_DIR" --version "$version"
done

for version in $(cat versions.json | jq -r '.versions[].version'); do
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think combining two for loops into one would be possible? What are the beenfits of doing it separately?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think should be okay to download ES snapshot and agent in the same loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went for that path in order to not block the existing code path if something fails with the elastic agent download

node x-pack/plugins/security_solution/scripts/endpoint/agent_downloader --version "$version"
done

echo "--- Cloning repos for docs build"
node scripts/validate_next_docs --clone-only
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy';
import { createEndpointHost } from '../../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data';

// FLAKY: https://github.com/elastic/kibana/issues/170667
describe.skip(
describe(
'Uninstall agent from host when agent tamper protection is disabled',
{ tags: ['@ess'] },
() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import { login } from '../../../tasks/login';
import { createEndpointHost } from '../../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data';

// FLAKY: https://github.com/elastic/kibana/issues/170601
describe.skip(
describe(
'Uninstall agent from host when agent tamper protection is enabled',
{ tags: ['@ess'] },
() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy';
import { createEndpointHost } from '../../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data';

// FLAKY: https://github.com/elastic/kibana/issues/170604
describe.skip(
describe(
'Uninstall agent from host changing agent policy when agent tamper protection is enabled but then is switched to a policy with it disabled',
{ tags: ['@ess'] },
() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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 { kibanaPackageJson } from '@kbn/repo-info';
import type { ToolingLog } from '@kbn/tooling-log';
import type { KbnClient } from '@kbn/test/src/kbn_client';
import { isFleetServerRunning } from '../../../../scripts/endpoint/common/fleet_server/fleet_server_services';
import type { HostVm } from '../../../../scripts/endpoint/common/types';
import type { BaseVmCreateOptions } from '../../../../scripts/endpoint/common/vm_services';
import { createVm } from '../../../../scripts/endpoint/common/vm_services';
import {
fetchAgentPolicyEnrollmentKey,
fetchFleetServerUrl,
getAgentDownloadUrl,
getAgentFileName,
getOrCreateDefaultAgentPolicy,
waitForHostToEnroll,
} from '../../../../scripts/endpoint/common/fleet_services';
import type { DownloadedAgentInfo } from '../../../../scripts/endpoint/common/agent_downloads_service';
import {
downloadAndStoreAgent,
isAgentDownloadFromDiskAvailable,
} from '../../../../scripts/endpoint/common/agent_downloads_service';

export interface CreateAndEnrollEndpointHostCIOptions
extends Pick<BaseVmCreateOptions, 'disk' | 'cpus' | 'memory'> {
kbnClient: KbnClient;
log: ToolingLog;
/** The fleet Agent Policy ID to use for enrolling the agent */
agentPolicyId: string;
/** version of the Agent to install. Defaults to stack version */
version?: string;
/** The name for the host. Will also be the name of the VM */
hostname?: string;
/** If `version` should be exact, or if this is `true`, then the closest version will be used. Defaults to `false` */
useClosestVersionMatch?: boolean;
}

export interface CreateAndEnrollEndpointHostCIResponse {
hostname: string;
agentId: string;
hostVm: HostVm;
}

/**
* Creates a new virtual machine (host) and enrolls that with Fleet
*/
export const createAndEnrollEndpointHostCI = async ({
kbnClient,
log,
agentPolicyId,
cpus,
disk,
memory,
hostname,
version = kibanaPackageJson.version,
useClosestVersionMatch = true,
}: CreateAndEnrollEndpointHostCIOptions): Promise<CreateAndEnrollEndpointHostCIResponse> => {
const vmName = hostname ?? `test-host-${Math.random().toString().substring(2, 6)}`;

const fileNameNoExtension = getAgentFileName(version);
const agentFileName = `${fileNameNoExtension}.tar.gz`;
let agentDownload: DownloadedAgentInfo | undefined;

// Check if agent file is already on disk before downloading it again
agentDownload = isAgentDownloadFromDiskAvailable(agentFileName);

// If it has not been already downloaded, it should be downloaded.
if (!agentDownload) {
log.warning(
`There is no agent installer for ${agentFileName} present on disk, trying to download it now.`
);
const { url: agentUrl } = await getAgentDownloadUrl(version, useClosestVersionMatch, log);
agentDownload = await downloadAndStoreAgent(agentUrl, agentFileName);
}

const hostVm = await createVm({
type: 'vagrant',
name: vmName,
log,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
agentDownload: agentDownload!,
disk,
cpus,
memory,
});

if (!(await isFleetServerRunning(kbnClient))) {
throw new Error(`Fleet server does not seem to be running on this instance of kibana!`);
}

const policyId = agentPolicyId || (await getOrCreateDefaultAgentPolicy({ kbnClient, log })).id;
const [fleetServerUrl, enrollmentToken] = await Promise.all([
fetchFleetServerUrl(kbnClient),
fetchAgentPolicyEnrollmentKey(kbnClient, policyId),
]);

const agentEnrollCommand = [
'sudo',

`./${fileNameNoExtension}/elastic-agent`,

'install',

'--insecure',

'--force',

'--url',
fleetServerUrl,

'--enrollment-token',
enrollmentToken,
].join(' ');

log.info(`Enrolling Elastic Agent with Fleet`);
log.verbose('Enrollment command:', agentEnrollCommand);

await hostVm.exec(agentEnrollCommand);

const { id: agentId } = await waitForHostToEnroll(kbnClient, log, hostVm.name, 240000);

return {
hostname: hostVm.name,
agentId,
hostVm,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ import {
import type { DeleteAllEndpointDataResponse } from '../../../../scripts/endpoint/common/delete_all_endpoint_data';
import { deleteAllEndpointData } from '../../../../scripts/endpoint/common/delete_all_endpoint_data';
import { waitForEndpointToStreamData } from '../../../../scripts/endpoint/common/endpoint_metadata_services';
import type {
CreateAndEnrollEndpointHostOptions,
CreateAndEnrollEndpointHostResponse,
} from '../../../../scripts/endpoint/common/endpoint_host_services';
import type { CreateAndEnrollEndpointHostResponse } from '../../../../scripts/endpoint/common/endpoint_host_services';
import {
createAndEnrollEndpointHost,
destroyEndpointHost,
Expand Down Expand Up @@ -66,6 +63,11 @@ import {
indexFleetEndpointPolicy,
} from '../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
import { cyLoadEndpointDataHandler } from './plugin_handlers/endpoint_data_loader';
import type {
CreateAndEnrollEndpointHostCIOptions,
CreateAndEnrollEndpointHostCIResponse,
} from './create_and_enroll_endpoint_host_ci';
import { createAndEnrollEndpointHostCI } from './create_and_enroll_endpoint_host_ci';

/**
* Test Role/User loader for cypress. Checks to see if running in serverless and handles it as appropriate
Expand Down Expand Up @@ -290,40 +292,48 @@ export const dataLoadersForRealEndpoints = (

on('task', {
createEndpointHost: async (
options: Omit<CreateAndEnrollEndpointHostOptions, 'log' | 'kbnClient'>
): Promise<CreateAndEnrollEndpointHostResponse> => {
options: Omit<CreateAndEnrollEndpointHostCIOptions, 'log' | 'kbnClient'>
): Promise<CreateAndEnrollEndpointHostCIResponse> => {
const { kbnClient, log } = await stackServicesPromise;

let retryAttempt = 0;
const attemptCreateEndpointHost = async (): Promise<CreateAndEnrollEndpointHostResponse> => {
try {
log.info(`Creating endpoint host, attempt ${retryAttempt}`);
const newHost = await createAndEnrollEndpointHost({
useClosestVersionMatch: true,
...options,
log,
kbnClient,
});
await waitForEndpointToStreamData(kbnClient, newHost.agentId, 360000);
return newHost;
} catch (err) {
log.info(`Caught error when setting up the agent: ${err}`);
if (retryAttempt === 0 && err.agentId) {
retryAttempt++;
await destroyEndpointHost(kbnClient, {
hostname: err.hostname || '', // No hostname in CI env for vagrant
agentId: err.agentId,
});
log.info(`Deleted endpoint host ${err.agentId} and retrying`);
return attemptCreateEndpointHost();
} else {
log.info(
`${retryAttempt} attempts of creating endpoint host failed, reason for the last failure was ${err}`
);
throw err;
const attemptCreateEndpointHost =
async (): Promise<CreateAndEnrollEndpointHostCIResponse> => {
try {
log.info(`Creating endpoint host, attempt ${retryAttempt}`);
const newHost = process.env.CI
? await createAndEnrollEndpointHostCI({
useClosestVersionMatch: true,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I understand correctly options can contain useClosestVersionMatch , do we want to let the hardcoded one be overwritten by options? What is the reason for this order, asking because this looks confusing to me :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even more confusing since we set useClosestVersionMatch to default to true in the function params

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a typo in the order introduced here. 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, this was a copy/paste of the existing code here. I'm ok removing this since the default value is true in a follow up pr if that looks good to you

...options,
log,
kbnClient,
})
: await createAndEnrollEndpointHost({
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can now remove the CI check and dependent code in createAndEnrollEndpointHost that also has a vagrant VM creation logic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I added a note in the description about doing that in a follow up pr.

useClosestVersionMatch: true,
...options,
log,
kbnClient,
});
await waitForEndpointToStreamData(kbnClient, newHost.agentId, 360000);
return newHost;
} catch (err) {
log.info(`Caught error when setting up the agent: ${err}`);
if (retryAttempt === 0 && err.agentId) {
retryAttempt++;
await destroyEndpointHost(kbnClient, {
hostname: err.hostname || '', // No hostname in CI env for vagrant
agentId: err.agentId,
});
log.info(`Deleted endpoint host ${err.agentId} and retrying`);
return attemptCreateEndpointHost();
} else {
log.info(
`${retryAttempt} attempts of creating endpoint host failed, reason for the last failure was ${err}`
);
throw err;
}
}
}
};
};

return attemptCreateEndpointHost();
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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.
*/

require('../../../../../src/setup_node_env');
require('./agent_downloader_cli').cli();
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 { ok } from 'assert';
import type { RunFn } from '@kbn/dev-cli-runner';
import type { ToolingLog } from '@kbn/tooling-log';
import { getAgentDownloadUrl, getAgentFileName } from '../common/fleet_services';
import { downloadAndStoreAgent } from '../common/agent_downloads_service';

const downloadAndStoreElasticAgent = async (
version: string,
closestMatch: boolean,
log: ToolingLog
) => {
const downloadUrlResponse = await getAgentDownloadUrl(version, closestMatch, log);
const fileNameNoExtension = getAgentFileName(version);
const agentFile = `${fileNameNoExtension}.tar.gz`;
await downloadAndStoreAgent(downloadUrlResponse.url, agentFile);
};

export const agentDownloaderRunner: RunFn = async (cliContext) => {
ok(cliContext.flags.version, 'version argument is required');
await downloadAndStoreElasticAgent(
cliContext.flags.version as string,
cliContext.flags.closestMatch as boolean,
cliContext.log
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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 { run } from '@kbn/dev-cli-runner';
import { agentDownloaderRunner } from './agent_downloader';

export const cli = () => {
run(
agentDownloaderRunner,

// Options
{
description: `Elastic Agent downloader`,
flags: {
string: ['version'],
boolean: ['closestMatch'],
default: {
closestMatch: true,
},
help: `
--version Required. Elastic agent version to be downloaded.
--closestMatch Optional. Use closest elastic agent version to match with.
`,
},
}
);
};
Loading