mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution][Endpoint] New enroll endpoint host function CI specific for Cypress tests to use cached agent files (#171399)
## Summary In order to avoid downloading the elastic agent installer file on each Cypress test, we have introduced a new method CI specific that will cache elastic agent files and reuse it across all tests. Old code about `if CI` conditions will be removed in a follow up pr. It also introduces a CLI script to download a specific version of elastic agent using the existing methods in place. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
d9ebfd9af1
commit
1823d94240
13 changed files with 307 additions and 53 deletions
|
@ -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
|
||||
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
|
||||
|
|
|
@ -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'] },
|
||||
() => {
|
||||
|
|
|
@ -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'] },
|
||||
() => {
|
||||
|
|
|
@ -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'] },
|
||||
() => {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
...options,
|
||||
log,
|
||||
kbnClient,
|
||||
})
|
||||
: 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return attemptCreateEndpointHost();
|
||||
},
|
||||
|
|
|
@ -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();
|
|
@ -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
|
||||
);
|
||||
};
|
|
@ -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.
|
||||
`,
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
|
@ -64,8 +64,10 @@ class AgentDownloadStorage extends SettingsStorage<AgentDownloadStorageSettings>
|
|||
}
|
||||
}
|
||||
|
||||
public getPathsForUrl(agentDownloadUrl: string): DownloadedAgentInfo {
|
||||
const filename = agentDownloadUrl.replace(/^https?:\/\//gi, '').replace(/\//g, '#');
|
||||
public getPathsForUrl(agentDownloadUrl: string, agentFileName?: string): DownloadedAgentInfo {
|
||||
const filename = agentFileName
|
||||
? agentFileName
|
||||
: agentDownloadUrl.replace(/^https?:\/\//gi, '').replace(/\//g, '#');
|
||||
const directory = this.downloadsDirFullPath;
|
||||
const fullFilePath = this.buildPath(join(this.downloadsDirName, filename));
|
||||
|
||||
|
@ -76,14 +78,17 @@ class AgentDownloadStorage extends SettingsStorage<AgentDownloadStorageSettings>
|
|||
};
|
||||
}
|
||||
|
||||
public async downloadAndStore(agentDownloadUrl: string): Promise<DownloadedAgentInfo> {
|
||||
public async downloadAndStore(
|
||||
agentDownloadUrl: string,
|
||||
agentFileName?: string
|
||||
): Promise<DownloadedAgentInfo> {
|
||||
this.log.debug(`Downloading and storing: ${agentDownloadUrl}`);
|
||||
|
||||
// TODO: should we add "retry" attempts to file downloads?
|
||||
|
||||
await this.ensureExists();
|
||||
|
||||
const newDownloadInfo = this.getPathsForUrl(agentDownloadUrl);
|
||||
const newDownloadInfo = this.getPathsForUrl(agentDownloadUrl, agentFileName);
|
||||
|
||||
// If download is already present on disk, then just return that info. No need to re-download it
|
||||
if (fs.existsSync(newDownloadInfo.fullFilePath)) {
|
||||
|
@ -154,6 +159,18 @@ class AgentDownloadStorage extends SettingsStorage<AgentDownloadStorageSettings>
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
public isAgentDownloadFromDiskAvailable(filename: string): DownloadedAgentInfo | undefined {
|
||||
if (fs.existsSync(join(this.downloadsDirFullPath, filename))) {
|
||||
return {
|
||||
filename,
|
||||
/** The local directory where downloads are stored */
|
||||
directory: this.downloadsDirFullPath,
|
||||
/** The full local file path and name */
|
||||
fullFilePath: join(this.downloadsDirFullPath, filename),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleProcessInterruptions = async <T>(
|
||||
|
@ -203,11 +220,16 @@ export interface DownloadAndStoreAgentResponse extends DownloadedAgentInfo {
|
|||
* already exists on disk, then no download is actually done - the information about the cached
|
||||
* version is returned instead
|
||||
* @param agentDownloadUrl
|
||||
* @param agentFileName
|
||||
*/
|
||||
export const downloadAndStoreAgent = async (
|
||||
agentDownloadUrl: string
|
||||
agentDownloadUrl: string,
|
||||
agentFileName?: string
|
||||
): Promise<DownloadAndStoreAgentResponse> => {
|
||||
const downloadedAgent = await agentDownloadsClient.downloadAndStore(agentDownloadUrl);
|
||||
const downloadedAgent = await agentDownloadsClient.downloadAndStore(
|
||||
agentDownloadUrl,
|
||||
agentFileName
|
||||
);
|
||||
|
||||
return {
|
||||
url: agentDownloadUrl,
|
||||
|
@ -221,3 +243,9 @@ export const downloadAndStoreAgent = async (
|
|||
export const cleanupDownloads = async (): ReturnType<AgentDownloadStorage['cleanupDownloads']> => {
|
||||
return agentDownloadsClient.cleanupDownloads();
|
||||
};
|
||||
|
||||
export const isAgentDownloadFromDiskAvailable = (
|
||||
fileName: string
|
||||
): DownloadedAgentInfo | undefined => {
|
||||
return agentDownloadsClient.isAgentDownloadFromDiskAvailable(fileName);
|
||||
};
|
||||
|
|
|
@ -373,6 +373,16 @@ export const getAgentVersionMatchingCurrentStack = async (
|
|||
return version;
|
||||
};
|
||||
|
||||
// Generates a file name using system arch and an agent version.
|
||||
export const getAgentFileName = (agentVersion: string): string => {
|
||||
const downloadArch =
|
||||
{ arm64: 'arm64', x64: 'x86_64' }[process.arch as string] ??
|
||||
`UNSUPPORTED_ARCHITECTURE_${process.arch}`;
|
||||
const fileName = `elastic-agent-${agentVersion}-linux-${downloadArch}`;
|
||||
|
||||
return fileName;
|
||||
};
|
||||
|
||||
interface ElasticArtifactSearchResponse {
|
||||
manifest: {
|
||||
'last-update-time': string;
|
||||
|
@ -414,11 +424,9 @@ export const getAgentDownloadUrl = async (
|
|||
log?: ToolingLog
|
||||
): Promise<GetAgentDownloadUrlResponse> => {
|
||||
const agentVersion = closestMatch ? await getLatestAgentDownloadVersion(version, log) : version;
|
||||
const downloadArch =
|
||||
{ arm64: 'arm64', x64: 'x86_64' }[process.arch as string] ??
|
||||
`UNSUPPORTED_ARCHITECTURE_${process.arch}`;
|
||||
const fileNameNoExtension = `elastic-agent-${agentVersion}-linux-${downloadArch}`;
|
||||
const agentFile = `${fileNameNoExtension}.tar.gz`;
|
||||
|
||||
const fileNameWithoutExtension = getAgentFileName(agentVersion);
|
||||
const agentFile = `${fileNameWithoutExtension}.tar.gz`;
|
||||
const artifactSearchUrl = `https://artifacts-api.elastic.co/v1/search/${agentVersion}/${agentFile}`;
|
||||
|
||||
log?.verbose(`Retrieving elastic agent download URL from:\n ${artifactSearchUrl}`);
|
||||
|
@ -444,7 +452,7 @@ export const getAgentDownloadUrl = async (
|
|||
return {
|
||||
url: searchResult.packages[agentFile].url,
|
||||
fileName: agentFile,
|
||||
dirName: fileNameNoExtension,
|
||||
dirName: fileNameWithoutExtension,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
hostname = ENV["VMNAME"] || 'ubuntu'
|
||||
cachedAgentSource = ENV["CACHED_AGENT_SOURCE"] || ''
|
||||
cachedAgentFilename = ENV["CACHED_AGENT_FILENAME"] || ''
|
||||
agentDestinationFolder = ENV["AGENT_DESTINATION_FOLDER"] || ''
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.hostname = hostname
|
||||
|
@ -29,6 +30,7 @@ Vagrant.configure("2") do |config|
|
|||
end
|
||||
|
||||
config.vm.provision "file", source: cachedAgentSource, destination: "~/#{cachedAgentFilename}"
|
||||
config.vm.provision "shell", inline: "tar -zxf #{cachedAgentFilename} && rm -f #{cachedAgentFilename}"
|
||||
config.vm.provision "shell", inline: "mkdir #{agentDestinationFolder}"
|
||||
config.vm.provision "shell", inline: "tar -zxf #{cachedAgentFilename} --directory #{agentDestinationFolder} --strip-components=1 && rm -f #{cachedAgentFilename}"
|
||||
config.vm.provision "shell", inline: "sudo apt-get install unzip"
|
||||
end
|
||||
|
|
|
@ -249,6 +249,7 @@ const createVagrantVm = async ({
|
|||
VMNAME: name,
|
||||
CACHED_AGENT_SOURCE: agentFullFilePath,
|
||||
CACHED_AGENT_FILENAME: agentFileName,
|
||||
AGENT_DESTINATION_FOLDER: agentFileName.replace('.tar.gz', ''),
|
||||
},
|
||||
// Only `pipe` STDERR to parent process
|
||||
stdio: ['inherit', 'inherit', 'pipe'],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue