mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 19:13:14 -04:00
[Fleet] Create and update package policy API return 409 conflict when names are not unique (#153533)
Closes https://github.com/elastic/kibana/issues/153026 ## Summary The agent policy API returns a 409 in the case the policy already exists. The package policy API will now follow the same behavior. ### Checklist Delete any items that are not applicable to this PR. - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
646221d1ac
commit
d75df24f96
9 changed files with 52 additions and 30 deletions
|
@ -3858,6 +3858,9 @@
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"$ref": "#/components/responses/error"
|
"$ref": "#/components/responses/error"
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"$ref": "#/components/responses/error"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
|
@ -4066,6 +4069,9 @@
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"$ref": "#/components/responses/error"
|
"$ref": "#/components/responses/error"
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"$ref": "#/components/responses/error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2406,6 +2406,8 @@ paths:
|
||||||
- item
|
- item
|
||||||
'400':
|
'400':
|
||||||
$ref: '#/components/responses/error'
|
$ref: '#/components/responses/error'
|
||||||
|
'409':
|
||||||
|
$ref: '#/components/responses/error'
|
||||||
requestBody:
|
requestBody:
|
||||||
description: >-
|
description: >-
|
||||||
You should use inputs as an object and not use the deprecated inputs
|
You should use inputs as an object and not use the deprecated inputs
|
||||||
|
@ -2537,6 +2539,8 @@ paths:
|
||||||
- success
|
- success
|
||||||
'400':
|
'400':
|
||||||
$ref: '#/components/responses/error'
|
$ref: '#/components/responses/error'
|
||||||
|
'409':
|
||||||
|
$ref: '#/components/responses/error'
|
||||||
/package_policies/upgrade/dryrun:
|
/package_policies/upgrade/dryrun:
|
||||||
post:
|
post:
|
||||||
summary: Dry run package policy upgrade
|
summary: Dry run package policy upgrade
|
||||||
|
|
|
@ -47,6 +47,8 @@ post:
|
||||||
- item
|
- item
|
||||||
'400':
|
'400':
|
||||||
$ref: ../components/responses/error.yaml
|
$ref: ../components/responses/error.yaml
|
||||||
|
'409':
|
||||||
|
$ref: ../components/responses/error.yaml
|
||||||
requestBody:
|
requestBody:
|
||||||
description: You should use inputs as an object and not use the deprecated inputs array.
|
description: You should use inputs as an object and not use the deprecated inputs array.
|
||||||
content:
|
content:
|
||||||
|
|
|
@ -36,3 +36,5 @@ post:
|
||||||
- success
|
- success
|
||||||
'400':
|
'400':
|
||||||
$ref: ../components/responses/error.yaml
|
$ref: ../components/responses/error.yaml
|
||||||
|
'409':
|
||||||
|
$ref: ../components/responses/error.yaml
|
||||||
|
|
|
@ -31,6 +31,7 @@ import {
|
||||||
PackageFailedVerificationError,
|
PackageFailedVerificationError,
|
||||||
PackagePolicyNotFoundError,
|
PackagePolicyNotFoundError,
|
||||||
FleetUnauthorizedError,
|
FleetUnauthorizedError,
|
||||||
|
PackagePolicyNameExistsError,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
|
||||||
type IngestErrorHandler = (
|
type IngestErrorHandler = (
|
||||||
|
@ -78,6 +79,9 @@ const getHTTPResponseCode = (error: FleetError): number => {
|
||||||
if (error instanceof FleetUnauthorizedError) {
|
if (error instanceof FleetUnauthorizedError) {
|
||||||
return 403; // Unauthorized
|
return 403; // Unauthorized
|
||||||
}
|
}
|
||||||
|
if (error instanceof PackagePolicyNameExistsError) {
|
||||||
|
return 409; // Conflict
|
||||||
|
}
|
||||||
return 400; // Bad Request
|
return 400; // Bad Request
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ export class ConcurrentInstallOperationError extends FleetError {}
|
||||||
export class AgentReassignmentError extends FleetError {}
|
export class AgentReassignmentError extends FleetError {}
|
||||||
export class PackagePolicyIneligibleForUpgradeError extends FleetError {}
|
export class PackagePolicyIneligibleForUpgradeError extends FleetError {}
|
||||||
export class PackagePolicyValidationError extends FleetError {}
|
export class PackagePolicyValidationError extends FleetError {}
|
||||||
|
export class PackagePolicyNameExistsError extends FleetError {}
|
||||||
export class PackagePolicyNotFoundError extends FleetError {}
|
export class PackagePolicyNotFoundError extends FleetError {}
|
||||||
export class BundledPackageNotFoundError extends FleetError {}
|
export class BundledPackageNotFoundError extends FleetError {}
|
||||||
export class HostedAgentPolicyRestrictionRelatedError extends FleetError {
|
export class HostedAgentPolicyRestrictionRelatedError extends FleetError {
|
||||||
|
|
|
@ -71,6 +71,7 @@ import {
|
||||||
PackagePolicyNotFoundError,
|
PackagePolicyNotFoundError,
|
||||||
HostedAgentPolicyRestrictionRelatedError,
|
HostedAgentPolicyRestrictionRelatedError,
|
||||||
FleetUnauthorizedError,
|
FleetUnauthorizedError,
|
||||||
|
PackagePolicyNameExistsError,
|
||||||
} from '../errors';
|
} from '../errors';
|
||||||
import { NewPackagePolicySchema, PackagePolicySchema, UpdatePackagePolicySchema } from '../types';
|
import { NewPackagePolicySchema, PackagePolicySchema, UpdatePackagePolicySchema } from '../types';
|
||||||
import type {
|
import type {
|
||||||
|
@ -166,17 +167,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
|
||||||
// trailing whitespace causes issues creating API keys
|
// trailing whitespace causes issues creating API keys
|
||||||
enrichedPackagePolicy.name = enrichedPackagePolicy.name.trim();
|
enrichedPackagePolicy.name = enrichedPackagePolicy.name.trim();
|
||||||
if (!options?.skipUniqueNameVerification) {
|
if (!options?.skipUniqueNameVerification) {
|
||||||
const existingPoliciesWithName = await this.list(soClient, {
|
await requireUniqueName(soClient, enrichedPackagePolicy);
|
||||||
perPage: 1,
|
|
||||||
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: "${enrichedPackagePolicy.name}"`,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check that the name does not exist already
|
|
||||||
if (existingPoliciesWithName.items.length > 0) {
|
|
||||||
throw new FleetError(
|
|
||||||
`An integration policy with the name ${enrichedPackagePolicy.name} already exists. Please rename it or choose a different name.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let elasticsearchPrivileges: NonNullable<PackagePolicy['elasticsearch']>['privileges'];
|
let elasticsearchPrivileges: NonNullable<PackagePolicy['elasticsearch']>['privileges'];
|
||||||
|
@ -548,17 +539,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
|
||||||
packagePolicy.name !== oldPackagePolicy.name &&
|
packagePolicy.name !== oldPackagePolicy.name &&
|
||||||
!options?.skipUniqueNameVerification
|
!options?.skipUniqueNameVerification
|
||||||
) {
|
) {
|
||||||
// Check that the name does not exist already but exclude the current package policy
|
await requireUniqueName(soClient, enrichedPackagePolicy, id);
|
||||||
const existingPoliciesWithName = await this.list(soClient, {
|
|
||||||
perPage: SO_SEARCH_LIMIT,
|
|
||||||
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name:"${packagePolicy.name}"`,
|
|
||||||
});
|
|
||||||
const filtered = (existingPoliciesWithName?.items || []).filter((p) => p.id !== id);
|
|
||||||
if (filtered.length > 0) {
|
|
||||||
throw new FleetError(
|
|
||||||
`An integration policy with the name ${packagePolicy.name} already exists. Please rename it or choose a different name.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputs = restOfPackagePolicy.inputs.map((input) =>
|
let inputs = restOfPackagePolicy.inputs.map((input) =>
|
||||||
|
@ -2251,3 +2232,25 @@ function deepMergeVars(original: any, override: any, keepOriginalValue = false):
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function requireUniqueName(
|
||||||
|
soClient: SavedObjectsClientContract,
|
||||||
|
packagePolicy: UpdatePackagePolicy | NewPackagePolicy,
|
||||||
|
id?: string
|
||||||
|
) {
|
||||||
|
const existingPoliciesWithName = await packagePolicyService.list(soClient, {
|
||||||
|
perPage: SO_SEARCH_LIMIT,
|
||||||
|
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name:"${packagePolicy.name}"`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const policiesToCheck = id
|
||||||
|
? // Check that the name does not exist already but exclude the current package policy
|
||||||
|
(existingPoliciesWithName?.items || []).filter((p) => p.id !== id)
|
||||||
|
: existingPoliciesWithName?.items;
|
||||||
|
|
||||||
|
if (policiesToCheck.length > 0) {
|
||||||
|
throw new PackagePolicyNameExistsError(
|
||||||
|
`An integration policy with the name ${packagePolicy.name} already exists. Please rename it or choose a different name.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -245,7 +245,7 @@ export default function (providerContext: FtrProviderContext) {
|
||||||
.expect(400);
|
.expect(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a 400 if there is another package policy with the same name', async function () {
|
it('should return a 409 if there is another package policy with the same name', async function () {
|
||||||
await supertest
|
await supertest
|
||||||
.post(`/api/fleet/package_policies`)
|
.post(`/api/fleet/package_policies`)
|
||||||
.set('kbn-xsrf', 'xxxx')
|
.set('kbn-xsrf', 'xxxx')
|
||||||
|
@ -279,10 +279,10 @@ export default function (providerContext: FtrProviderContext) {
|
||||||
version: '0.1.0',
|
version: '0.1.0',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.expect(400);
|
.expect(409);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a 400 if there is a package policy with the same name on a different policy', async function () {
|
it('should return a 409 if there is a package policy with the same name on a different policy', async function () {
|
||||||
const { body: agentPolicyResponse } = await supertest
|
const { body: agentPolicyResponse } = await supertest
|
||||||
.post(`/api/fleet/agent_policies`)
|
.post(`/api/fleet/agent_policies`)
|
||||||
.set('kbn-xsrf', 'xxxx')
|
.set('kbn-xsrf', 'xxxx')
|
||||||
|
@ -325,7 +325,7 @@ export default function (providerContext: FtrProviderContext) {
|
||||||
version: '0.1.0',
|
version: '0.1.0',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.expect(400);
|
.expect(409);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a 400 with required variables not provided', async function () {
|
it('should return a 400 with required variables not provided', async function () {
|
||||||
|
|
|
@ -368,7 +368,7 @@ export default function (providerContext: FtrProviderContext) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a 400 if there is another package policy with the same name', async function () {
|
it('should return a 409 if there is another package policy with the same name', async function () {
|
||||||
await supertest
|
await supertest
|
||||||
.put(`/api/fleet/package_policies/${packagePolicyId2}`)
|
.put(`/api/fleet/package_policies/${packagePolicyId2}`)
|
||||||
.set('kbn-xsrf', 'xxxx')
|
.set('kbn-xsrf', 'xxxx')
|
||||||
|
@ -385,10 +385,10 @@ export default function (providerContext: FtrProviderContext) {
|
||||||
version: '0.1.0',
|
version: '0.1.0',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.expect(400);
|
.expect(409);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a 400 if there is another package policy with the same name on a different policy', async function () {
|
it('should return a 409 if there is another package policy with the same name on a different policy', async function () {
|
||||||
const { body: agentPolicyResponse } = await supertest
|
const { body: agentPolicyResponse } = await supertest
|
||||||
.post(`/api/fleet/agent_policies`)
|
.post(`/api/fleet/agent_policies`)
|
||||||
.set('kbn-xsrf', 'xxxx')
|
.set('kbn-xsrf', 'xxxx')
|
||||||
|
@ -414,7 +414,7 @@ export default function (providerContext: FtrProviderContext) {
|
||||||
version: '0.1.0',
|
version: '0.1.0',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.expect(400);
|
.expect(409);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with frozen input vars', async function () {
|
it('should work with frozen input vars', async function () {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue