Skip to content
Merged
324 changes: 324 additions & 0 deletions oas_docs/bundle.json

Large diffs are not rendered by default.

324 changes: 324 additions & 0 deletions oas_docs/bundle.serverless.json

Large diffs are not rendered by default.

216 changes: 216 additions & 0 deletions oas_docs/output/kibana.serverless.yaml

Large diffs are not rendered by default.

216 changes: 216 additions & 0 deletions oas_docs/output/kibana.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions x-pack/platform/plugins/shared/fleet/common/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export class FleetActionsClientError extends FleetError {}
export class UninstallTokenError extends FleetError {}

export class AgentRequestInvalidError extends FleetError {}
export class OutputInvalidError extends FleetError {}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ export function getDefaultFleetServerpolicyId(spaceId?: string) {
export function policyHasFleetServer(
agentPolicy: Pick<AgentPolicy, 'package_policies' | 'has_fleet_server'>
) {
if (!agentPolicy.package_policies) {
return false;
}
return (
agentPolicy.package_policies?.some((p) => p.package?.name === FLEET_SERVER_PACKAGE) ||
!!agentPolicy.has_fleet_server
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ describe('getAllowedOutputTypesForAgentPolicy', () => {
expect(res).toEqual(['elasticsearch']);
});

it('should return only elasticsearch for an agent policy with Fleet Server not yet installed', () => {
const res = getAllowedOutputTypesForAgentPolicy({
has_fleet_server: true,
} as any);

expect(res).toEqual(['elasticsearch']);
});

it('should return only elasticsearch for an agentless agent policy', () => {
const res = getAllowedOutputTypesForAgentPolicy({ supports_agentless: true } as any);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ const sameClusterRestrictedPackages = [
*/
export function getAllowedOutputTypesForAgentPolicy(agentPolicy: Partial<AgentPolicy>): string[] {
const isRestrictedToSameClusterES =
agentPolicy.package_policies &&
agentPolicy.package_policies.some(
(p) => p.package?.name && sameClusterRestrictedPackages.includes(p.package?.name)
);
agentPolicy.has_fleet_server ||
(agentPolicy.package_policies &&
agentPolicy.package_policies.some(
(p) => p.package?.name && sameClusterRestrictedPackages.includes(p.package?.name)
));

if (isRestrictedToSameClusterES) {
return [outputType.Elasticsearch];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import { useState, useCallback, useEffect, useMemo } from 'react';
import { i18n } from '@kbn/i18n';

import { OutputInvalidError } from '../../../../../../common/errors';
import { getDefaultFleetServerpolicyId } from '../../../../../../common/services/agent_policies_helpers';
import type { useComboInput, useInput, useSwitchInput } from '../../../hooks';
import {
sendCreateAgentPolicy,
sendCreateAgentPolicyForRq,
sendGetOneAgentPolicy,
useFleetStatus,
useStartServices,
Expand Down Expand Up @@ -115,23 +116,40 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => {
if (existingPolicy.data?.item) {
setFleetServerPolicyId(existingPolicy.data?.item.id);
} else {
const createPolicyResponse = await sendCreateAgentPolicy(
const createPolicyResponse = await sendCreateAgentPolicyForRq(
quickStartFleetServerPolicyFields,
{
withSysMonitoring: true,
}
);
setFleetServerPolicyId(createPolicyResponse.data?.item.id);
setFleetServerPolicyId(createPolicyResponse.item.id);
}

setStatus('success');
}
} catch (err) {
notifications.toasts.addError(err, {
title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', {
defaultMessage: 'Error adding Fleet Server host',
}),
});
if (err?.attributes?.type === OutputInvalidError.name) {
notifications.toasts.addError(err, {
title: i18n.translate(
'xpack.fleet.fleetServerSetup.errorCreatingFleetServerPolicyTitle',
{
defaultMessage: 'Error creating a Fleet Server policy',
}
),
toastMessage: i18n.translate(
'xpack.fleet.fleetServerSetup.errorCreatingFleetServerPolicyMessage',
{
defaultMessage:
'Fleet Server policy creation failed as your default output is not an elasticsearch output. Use the advanced section to use an elasticsearch output to create that policy.',
}
),
});
} else {
notifications.toasts.addError(err, {
title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', {
defaultMessage: 'Error adding Fleet Server host',
}),
});
}

setStatus('error');
setError(err.message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export function useOutputOptions(agentPolicy: Partial<NewAgentPolicy | AgentPoli
licenseService.hasAtLeast(LICENCE_FOR_PER_POLICY_OUTPUT) ||
policyHasFleetServer(agentPolicy as AgentPolicy) ||
policyHasSyntheticsIntegration(agentPolicy as AgentPolicy);

const allowedOutputTypes = useMemo(
() => getAllowedOutputTypesForAgentPolicy(agentPolicy as AgentPolicy),
[agentPolicy]
Expand Down Expand Up @@ -146,13 +147,21 @@ export function useOutputOptions(agentPolicy: Partial<NewAgentPolicy | AgentPoli
];
}, [outputsRequest, isPolicyPerOutputAllowed]);

const dataOutputValueOfSelected = agentPolicy.data_output_id || DEFAULT_SELECT_VALUE;

return useMemo(
() => ({
dataOutputOptions,
monitoringOutputOptions,
dataOutputValueOfSelected,
isLoading: outputsRequest.isLoading,
}),
[dataOutputOptions, monitoringOutputOptions, outputsRequest.isLoading]
[
dataOutputOptions,
dataOutputValueOfSelected,
monitoringOutputOptions,
outputsRequest.isLoading,
]
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =

const {
dataOutputOptions,
dataOutputValueOfSelected,
monitoringOutputOptions,
isLoading: isLoadingOptions,
} = useOutputOptions(agentPolicy);
Expand Down Expand Up @@ -657,7 +658,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
>
<EuiSuperSelect
disabled={disabled || isManagedPolicy}
valueOfSelected={agentPolicy.data_output_id || DEFAULT_SELECT_VALUE}
valueOfSelected={dataOutputValueOfSelected}
fullWidth
isLoading={isLoadingOptions}
onChange={(e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { i18n } from '@kbn/i18n';

import { useSpaceSettingsContext } from '../../../../../hooks/use_space_settings_context';
import type { AgentPolicy, NewAgentPolicy } from '../../../types';
import { sendCreateAgentPolicy, useStartServices, useAuthz } from '../../../hooks';
import { useStartServices, useAuthz, sendCreateAgentPolicyForRq } from '../../../hooks';
import { generateNewAgentPolicyWithDefaults } from '../../../../../../common/services/generate_new_agent_policy';

import { agentPolicyFormValidation } from '.';
Expand All @@ -48,7 +48,7 @@ export const AgentPolicyCreateInlineForm: React.FunctionComponent<Props> = ({
isFleetServerPolicy,
agentPolicyName,
}) => {
const { docLinks } = useStartServices();
const { docLinks, notifications } = useStartServices();
const authz = useAuthz();
const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({});

Expand Down Expand Up @@ -83,17 +83,20 @@ export const AgentPolicyCreateInlineForm: React.FunctionComponent<Props> = ({
const createAgentPolicy = useCallback(async () => {
try {
setIsLoading(true);
const resp = await sendCreateAgentPolicy(newAgentPolicy, { withSysMonitoring });
if (resp.error) throw resp.error;
if (resp.data) {
updateAgentPolicy(resp.data.item);
}
const data = await sendCreateAgentPolicyForRq(newAgentPolicy, { withSysMonitoring });

updateAgentPolicy(data.item);
} catch (e) {
notifications.toasts.addError(e, {
title: i18n.translate('xpack.fleet.agentPolicyCreateInline.errorTitle', {
defaultMessage: 'Error creating agent policy',
}),
});
updateAgentPolicy(null, mapError(e));
} finally {
setIsLoading(false);
}
}, [newAgentPolicy, withSysMonitoring, updateAgentPolicy]);
}, [newAgentPolicy, withSysMonitoring, updateAgentPolicy, notifications.toasts]);

function mapError(e: { statusCode: number }): JSX.Element | undefined {
switch (e.statusCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,22 @@ export function useGetAutoUpgradeAgentsStatusQuery(agentPolicyId: string) {
);
}

export const sendCreateAgentPolicyForRq = (
body: CreateAgentPolicyRequest['body'],
{ withSysMonitoring }: { withSysMonitoring: boolean } = { withSysMonitoring: false }
) => {
return sendRequestForRq<CreateAgentPolicyResponse>({
path: agentPolicyRouteService.getCreatePath(),
method: 'post',
body: JSON.stringify(body),
query: withSysMonitoring ? { sys_monitoring: true } : {},
version: API_VERSIONS.public.v1,
});
};

/**
* @deprecated use sendCreateAgentPolicyForRq instead
*/
export const sendCreateAgentPolicy = (
body: CreateAgentPolicyRequest['body'],
{ withSysMonitoring }: { withSysMonitoring: boolean } = { withSysMonitoring: false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
AgentlessPolicyExistsRequestError,
PackageInvalidDeploymentMode,
PackagePolicyContentPackageError,
OutputInvalidError,
} from '.';

type IngestErrorHandler = (
Expand Down Expand Up @@ -154,6 +155,13 @@ const getHTTPResponseCode = (error: FleetError): number => {
return 400; // Bad Request
};

function shouldRespondWithErrorType(error: FleetError) {
if (error instanceof OutputInvalidError) {
return true;
}
return false;
}

export function fleetErrorToResponseOptions(error: IngestErrorHandlerParams['error']) {
const logger = appContextService.getLogger();
// our "expected" errors
Expand All @@ -164,6 +172,7 @@ export function fleetErrorToResponseOptions(error: IngestErrorHandlerParams['err
statusCode: getHTTPResponseCode(error),
body: {
message: error.message,
...(shouldRespondWithErrorType(error) ? { attributes: { type: error.name } } : {}),
...(error.attributes && { attributes: error.attributes }),
},
};
Expand Down
6 changes: 4 additions & 2 deletions x-pack/platform/plugins/shared/fleet/server/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ export {
} from './handlers';

export { isESClientError } from './utils';
export { FleetError as FleetError } from '../../common/errors';
export {
FleetError as FleetError,
OutputInvalidError as OutputInvalidError,
} from '../../common/errors';

export class RegistryError extends FleetError {}
export class RegistryConnectionError extends RegistryError {}
Expand Down Expand Up @@ -115,7 +118,6 @@ export class FleetNotFoundError<TMeta = unknown> extends FleetError<TMeta> {}
export class FleetTooManyRequestsError extends FleetError {}

export class OutputUnauthorizedError extends FleetError {}
export class OutputInvalidError extends FleetError {}
export class OutputLicenceError extends FleetError {}
export class DownloadSourceError extends FleetError {}
export class DeleteUnenrolledAgentsPreconfiguredError extends FleetError {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const genericErrorResponse = () =>
{
statusCode: schema.maybe(schema.number()),
error: schema.maybe(schema.string()),
errorType: schema.maybe(schema.string()),
message: schema.string(),
attributes: schema.maybe(schema.any()),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export async function validateOutputForPolicy(
allowedOutputTypeForPolicy: string[] = Object.values(outputType)
) {
if (
Object.keys(existingData).length !== 0 &&
newData.data_output_id === existingData.data_output_id &&
newData.monitoring_output_id === existingData.monitoring_output_id
) {
Expand All @@ -66,7 +67,15 @@ export async function validateOutputForPolicy(
allowedOutputTypeForPolicy.length !== Object.values(outputType).length;

if (isOutputTypeRestricted) {
const dataOutput = await getDataOutputForAgentPolicy(soClient, data);
const dataOutput = await getDataOutputForAgentPolicy(soClient, data).catch((err) => {
if (err instanceof OutputNotFoundError) {
return;
}
throw err;
});
if (!dataOutput) {
return;
}
if (!allowedOutputTypeForPolicy.includes(dataOutput.type)) {
throw new OutputInvalidError(
`Output of type "${dataOutput.type}" is not usable with policy "${data.name}".`
Expand Down Expand Up @@ -116,7 +125,15 @@ export async function validateAgentPolicyOutputForIntegration(
allowedOutputTypeForPolicy.length !== Object.values(outputType).length;

if (isOutputTypeRestricted) {
const dataOutput = await getDataOutputForAgentPolicy(soClient, agentPolicy);
const dataOutput = await getDataOutputForAgentPolicy(soClient, agentPolicy).catch((err) => {
if (err instanceof OutputNotFoundError) {
return;
}
throw err;
});
if (!dataOutput) {
return;
}
if (!allowedOutputTypeForPolicy.includes(dataOutput.type)) {
if (isNewPackagePolicy) {
throw new OutputInvalidError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ class AgentPolicyService {
user?: AuthenticatedUser;
authorizationHeader?: HTTPAuthorizationHeader | null;
skipDeploy?: boolean;
hasFleetServer?: boolean;
} = {}
): Promise<AgentPolicy> {
const savedObjectType = await getAgentPolicySavedObjectType();
Expand Down Expand Up @@ -412,12 +413,17 @@ class AgentPolicyService {
spaceId: soClient.getCurrentNamespace(),
namespace: agentPolicy.namespace,
});
const policyForOutputValidation = {
...agentPolicy,
has_fleet_server: options?.hasFleetServer,
};
await validateOutputForPolicy(
soClient,
agentPolicy,
policyForOutputValidation,
{},
getAllowedOutputTypesForAgentPolicy(agentPolicy)
getAllowedOutputTypesForAgentPolicy(policyForOutputValidation)
);

validateRequiredVersions(agentPolicy.name, agentPolicy.required_versions);

const newSo = await soClient.create<AgentPolicySOAttributes>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export async function createAgentPolicyWithPackages({
user,
id: agentPolicyId,
authorizationHeader,
hasFleetServer,
skipDeploy: true, // skip deploying the policy until package policies are added
});

Expand Down
Loading
Loading