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] Present users with a Kubernetes manifest for deploying agent in fleet managed mode for Kubernetes #127703

Merged
Merged
7 changes: 7 additions & 0 deletions x-pack/plugins/fleet/common/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const EPM_API_ROOT = `${API_ROOT}/epm`;
export const DATA_STREAM_API_ROOT = `${API_ROOT}/data_streams`;
export const PACKAGE_POLICY_API_ROOT = `${API_ROOT}/package_policies`;
export const AGENT_POLICY_API_ROOT = `${API_ROOT}/agent_policies`;
export const K8S_API_ROOT = `${API_ROOT}/kubernetes`;

export const LIMITED_CONCURRENCY_ROUTE_TAG = 'ingest:limited-concurrency';

Expand Down Expand Up @@ -67,6 +68,12 @@ export const AGENT_POLICY_API_ROUTES = {
FULL_INFO_DOWNLOAD_PATTERN: `${AGENT_POLICY_API_ROOT}/{agentPolicyId}/download`,
};

// Kubernetes Manifest API routes
export const K8S_API_ROUTES = {
K8S_DOWNLOAD_PATTERN: `${K8S_API_ROOT}/download`,
K8S_INFO_PATTERN: `${K8S_API_ROOT}`,
};

// Output API routes
export const OUTPUT_API_ROUTES = {
LIST_PATTERN: `${API_ROOT}/outputs`,
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/fleet/common/services/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
OUTPUT_API_ROUTES,
SETTINGS_API_ROUTES,
APP_API_ROUTES,
K8S_API_ROUTES,
} from '../constants';

export const epmRouteService = {
Expand Down Expand Up @@ -141,6 +142,14 @@ export const agentPolicyRouteService = {
agentPolicyId
);
},

getK8sInfoPath: () => {
return K8S_API_ROUTES.K8S_INFO_PATTERN;
},

getK8sFullDownloadPath: () => {
return K8S_API_ROUTES.K8S_DOWNLOAD_PATTERN;
},
};

export const dataStreamRouteService = {
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@ export interface GetFullAgentPolicyResponse {
export interface GetFullAgentConfigMapResponse {
item: string;
}

export interface GetFullAgentManifestResponse {
item: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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 { useState, useEffect } from 'react';
import { i18n } from '@kbn/i18n';

import type { PackagePolicy, AgentPolicy } from '../../types';
import { sendGetOneAgentPolicy, useStartServices } from '../../hooks';
import { FLEET_KUBERNETES_PACKAGE } from '../../../common';

export function useAgentPolicyWithPackagePolicies(policyId?: string) {
const [agentPolicyWithPackagePolicies, setAgentPolicy] = useState<AgentPolicy | null>(null);
const core = useStartServices();
const { notifications } = core;

useEffect(() => {
async function loadPolicy(policyIdToLoad?: string) {
if (!policyIdToLoad) {
return;
}
try {
const agentPolicyRequest = await sendGetOneAgentPolicy(policyIdToLoad);

setAgentPolicy(agentPolicyRequest.data ? agentPolicyRequest.data.item : null);
} catch (err) {
notifications.toasts.addError(err, {
title: i18n.translate('xpack.fleet.agentEnrollment.loadPolicyErrorMessage', {
defaultMessage: 'An error happened while loading the policy',
}),
});
}
}

loadPolicy(policyId);
}, [policyId, notifications.toasts]);

return { agentPolicyWithPackagePolicies };
}

export function useIsK8sPolicy(agentPolicy?: AgentPolicy) {
const [isK8s, setIsK8s] = useState<'IS_LOADING' | 'IS_KUBERNETES' | 'IS_NOT_KUBERNETES'>(
'IS_LOADING'
);
useEffect(() => {
async function checkifK8s() {
if (!agentPolicy) {
setIsK8s('IS_LOADING');
return;
}

setIsK8s(
(agentPolicy.package_policies as PackagePolicy[]).some(isK8sPackage)
? 'IS_KUBERNETES'
: 'IS_NOT_KUBERNETES'
);
}
checkifK8s();
}, [agentPolicy]);

return { isK8s };
}

const isK8sPackage = (pkg: PackagePolicy) => pkg.package?.name === FLEET_KUBERNETES_PACKAGE;
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,7 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';

import {
useGetSettings,
sendGetOneAgentPolicy,
useFleetStatus,
useAgentEnrollmentFlyoutData,
} from '../../hooks';
import { useGetSettings, useFleetStatus, useAgentEnrollmentFlyoutData } from '../../hooks';
import { FLEET_SERVER_PACKAGE } from '../../constants';
import type { PackagePolicy } from '../../types';

Expand All @@ -37,6 +32,7 @@ import { ManagedInstructions } from './managed_instructions';
import { StandaloneInstructions } from './standalone_instructions';
import { MissingFleetServerHostCallout } from './missing_fleet_server_host_callout';
import type { BaseProps } from './types';
import { useIsK8sPolicy, useAgentPolicyWithPackagePolicies } from './hooks';

type FlyoutMode = 'managed' | 'standalone';

Expand Down Expand Up @@ -72,26 +68,24 @@ export const AgentEnrollmentFlyout: React.FunctionComponent<Props> = ({
isLoadingAgentPolicies,
refreshAgentPolicies,
} = useAgentEnrollmentFlyoutData();

const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(policyId);
useEffect(() => {
async function checkPolicyIsFleetServer() {
if (policyId && setIsFleetServerPolicySelected) {
const agentPolicyRequest = await sendGetOneAgentPolicy(policyId);
if (
agentPolicyRequest.data?.item &&
(agentPolicyRequest.data.item.package_policies as PackagePolicy[]).some(
(packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE
)
) {
setIsFleetServerPolicySelected(true);
} else {
setIsFleetServerPolicySelected(false);
}
if (agentPolicyWithPackagePolicies && setIsFleetServerPolicySelected) {
if (
(agentPolicyWithPackagePolicies.package_policies as PackagePolicy[]).some(
(packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE
)
) {
setIsFleetServerPolicySelected(true);
} else {
setIsFleetServerPolicySelected(false);
}
}
}, [agentPolicyWithPackagePolicies]);

checkPolicyIsFleetServer();
}, [policyId]);
const { isK8s } = useIsK8sPolicy(
agentPolicyWithPackagePolicies ? agentPolicyWithPackagePolicies : undefined
);

const isLoadingInitialRequest = settings.isLoading && settings.isInitialRequest;

Expand Down Expand Up @@ -154,10 +148,12 @@ export const AgentEnrollmentFlyout: React.FunctionComponent<Props> = ({
<ManagedInstructions
settings={settings.data?.item}
setSelectedPolicyId={setSelectedPolicyId}
policyId={policyId}
agentPolicy={agentPolicy}
agentPolicies={agentPolicies}
viewDataStep={viewDataStep}
isFleetServerPolicySelected={isFleetServerPolicySelected}
isK8s={isK8s}
refreshAgentPolicies={refreshAgentPolicies}
isLoadingAgentPolicies={isLoadingAgentPolicies}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ export const ManagedInstructions = React.memo<InstructionProps>(
agentPolicies,
viewDataStep,
setSelectedPolicyId,
policyId,
isFleetServerPolicySelected,
isK8s,
settings,
refreshAgentPolicies,
isLoadingAgentPolicies,
}) => {
const fleetStatus = useFleetStatus();

const [selectedApiKeyId, setSelectedAPIKeyId] = useState<string | undefined>();

const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId);
const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id);

Expand Down Expand Up @@ -111,6 +111,8 @@ export const ManagedInstructions = React.memo<InstructionProps>(
];
}, [fleetServerInstructions]);

const enrolToken = apiKey.data ? apiKey.data.item.api_key : '';

const steps = useMemo(() => {
const fleetServerHosts = settings?.fleet_server_hosts || [];
const baseSteps: EuiContainedStepProps[] = [
Expand All @@ -123,7 +125,7 @@ export const ManagedInstructions = React.memo<InstructionProps>(
refreshAgentPolicies,
})
: AgentEnrollmentKeySelectionStep({ agentPolicy, selectedApiKeyId, setSelectedAPIKeyId }),
DownloadStep(isFleetServerPolicySelected || false),
DownloadStep(isFleetServerPolicySelected || false, isK8s || '', enrolToken || ''),
];
if (isFleetServerPolicySelected) {
baseSteps.push(...fleetServerSteps);
Expand All @@ -133,7 +135,12 @@ export const ManagedInstructions = React.memo<InstructionProps>(
defaultMessage: 'Enroll and start the Elastic Agent',
}),
children: selectedApiKeyId && apiKey.data && (
<ManualInstructions apiKey={apiKey.data.item} fleetServerHosts={fleetServerHosts} />
<ManualInstructions
apiKey={apiKey.data.item}
fleetServerHosts={fleetServerHosts}
policyId={policyId}
isK8s={isK8s}
/>
),
});
}
Expand All @@ -155,6 +162,9 @@ export const ManagedInstructions = React.memo<InstructionProps>(
isFleetServerPolicySelected,
settings?.fleet_server_hosts,
viewDataStep,
enrolToken,
isK8s,
policyId,
]);

if (fleetStatus.isReady && settings?.fleet_server_hosts.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,15 @@ import {
useStartServices,
useLink,
sendGetOneAgentPolicyFull,
sendGetOneAgentPolicy,
useKibanaVersion,
} from '../../hooks';
import { fullAgentPolicyToYaml, agentPolicyRouteService } from '../../services';

import type { PackagePolicy } from '../../../common';

import { FLEET_KUBERNETES_PACKAGE } from '../../../common';

import { PlatformSelector } from '../enrollment_instructions/manual/platform_selector';

import { DownloadStep, AgentPolicySelectionStep } from './steps';
import type { InstructionProps } from './types';
import { useIsK8sPolicy, useAgentPolicyWithPackagePolicies } from './hooks';

export const StandaloneInstructions = React.memo<InstructionProps>(
({ agentPolicy, agentPolicies, refreshAgentPolicies }) => {
Expand All @@ -49,12 +45,14 @@ export const StandaloneInstructions = React.memo<InstructionProps>(

const [selectedPolicyId, setSelectedPolicyId] = useState<string | undefined>(agentPolicy?.id);
const [fullAgentPolicy, setFullAgentPolicy] = useState<any | undefined>();
const [isK8s, setIsK8s] = useState<'IS_LOADING' | 'IS_KUBERNETES' | 'IS_NOT_KUBERNETES'>(
'IS_LOADING'
);
const [yaml, setYaml] = useState<string | string>('');
const kibanaVersion = useKibanaVersion();

const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(selectedPolicyId);
const { isK8s } = useIsK8sPolicy(
agentPolicyWithPackagePolicies ? agentPolicyWithPackagePolicies : undefined
);

const KUBERNETES_RUN_INSTRUCTIONS = 'kubectl apply -f elastic-agent-standalone-kubernetes.yaml';

const STANDALONE_RUN_INSTRUCTIONS_LINUX = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz
Expand Down Expand Up @@ -84,28 +82,6 @@ sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm \nsudo systemctl enable e

const { docLinks } = useStartServices();

useEffect(() => {
async function checkifK8s() {
if (!selectedPolicyId) {
return;
}
const agentPolicyRequest = await sendGetOneAgentPolicy(selectedPolicyId);
const agentPol = agentPolicyRequest.data ? agentPolicyRequest.data.item : null;

if (!agentPol) {
setIsK8s('IS_NOT_KUBERNETES');
return;
}
const k8s = (pkg: PackagePolicy) => pkg.package?.name === FLEET_KUBERNETES_PACKAGE;
setIsK8s(
(agentPol.package_policies as PackagePolicy[]).some(k8s)
? 'IS_KUBERNETES'
: 'IS_NOT_KUBERNETES'
);
}
checkifK8s();
}, [selectedPolicyId, notifications.toasts]);

useEffect(() => {
async function fetchFullPolicy() {
try {
Expand Down Expand Up @@ -178,7 +154,9 @@ sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm \nsudo systemctl enable e
downloadLink =
isK8s === 'IS_KUBERNETES'
? core.http.basePath.prepend(
`${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicyId)}?kubernetes=true`
`${agentPolicyRouteService.getInfoFullDownloadPath(
selectedPolicyId
)}?kubernetes=true&standalone=true`
)
: core.http.basePath.prepend(
`${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicyId)}?standalone=true`
Expand Down
Loading