[ui] Scope selection for Sentinel Policies (#25390)

* An option to select, and column etc. to view, sentinel policy scope

* Flake potential: Seed(1) had a couple jobs with the same ModifyIndex

* More de-flaking
This commit is contained in:
Phil Renaud
2025-03-14 12:37:39 -04:00
committed by GitHub
parent e3f21166af
commit 88ff5a7cae
11 changed files with 74 additions and 3 deletions

3
.changelog/25390.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:improvement
ui: Added a scope selector for sentinel policy page
```

View File

@@ -77,6 +77,18 @@
</Hds::Form::Radio::Group>
</div>
<div>
<Hds::Form::Radio::Group @layout="horizontal" @name="method-demo1" {{on "change" this.updatePolicyScope}} as |G|>
<G.Legend>Scope</G.Legend>
<G.RadioField @id="submit-job" checked={{eq @policy.scope "submit-job"}} data-test-scope="submit-job" as |F|>
<F.Label>Submit Job</F.Label>
</G.RadioField>
<G.RadioField @id="submit-host-volume" checked={{eq @policy.scope "submit-host-volume"}} data-test-scope="submit-host-volume" as |F|>
<F.Label>Submit Host Volume</F.Label>
</G.RadioField>
</Hds::Form::Radio::Group>
</div>
<footer>
{{#if (can "update sentinel-policy")}}
<Hds::Button

View File

@@ -30,6 +30,10 @@ export default class SentinelPolicyEditorComponent extends Component {
this.policy.set('enforcementLevel', id);
}
@action updatePolicyScope({ target: { id } }) {
this.policy.set('scope', id);
}
@action async save(e) {
if (e instanceof Event) {
e.preventDefault(); // code-mirror "command+enter" submits the form, but doesnt have a preventDefault()

View File

@@ -43,6 +43,11 @@ export default class SentinelPoliciesIndexController extends Controller {
label: 'Enforcement Level',
isSortable: true,
},
{
key: 'scope',
label: 'Scope',
isSortable: true,
},
{
key: 'delete',
label: 'Delete',

View File

@@ -46,6 +46,7 @@ SPDX-License-Identifier: BUSL-1.1
</B.Td>
<B.Td data-test-sentinel-policy-description>{{B.data.description}}</B.Td>
<B.Td data-test-sentinel-policy-enforcement>{{B.data.enforcementLevel}}</B.Td>
<B.Td data-test-sentinel-policy-scope>{{B.data.scope}}</B.Td>
{{#if (can "destroy sentinel-policy")}}
<B.Td>
<TwoStepButton

View File

@@ -22,6 +22,6 @@ export default Factory.extend({
main = rule { false }`,
scope: 'submit-job',
scope: pickOne(['submit-job', 'submit-host-volume']),
enforcementLevel: pickOne(['advisory', 'soft-mandatory', 'hard-mandatory']),
});

View File

@@ -685,6 +685,21 @@ function policiesTestCluster(server, options = { sentinel: false }) {
scope: 'submit-job',
});
server.create('sentinel-policy', {
id: 'host-volume-policy',
name: 'host-volume-policy',
description: 'A sentinel policy generated by Mirage',
enforcementLevel: 'soft-mandatory',
policy: `
has_tag = func() {
print("volume is missing tag")
tag = volume.parameters["tag"] else 0
return tag is not 0
}
main = rule { has_tag() }
`,
scope: 'submit-host-volume',
});
server.createList('sentinel-policy', 5);
}

View File

@@ -167,7 +167,11 @@ moduleForJobWithClientStatus(
moduleForJob(
'Acceptance | job detail (periodic)',
'children',
() => server.create('job', 'periodic', { shallow: true }),
() =>
server.create('job', 'periodic', {
shallow: true,
withPreviousStableVersion: true,
}),
{
'the default sort is submitTime descending': async function (job, assert) {
const mostRecentLaunch = server.db.jobs

View File

@@ -52,7 +52,7 @@ module('Acceptance | jobs list', function (hooks) {
});
test('/jobs should list the first page of jobs sorted by modify index', async function (assert) {
faker.seed(1);
faker.seed(2);
const jobsCount = JobsList.pageSize + 1;
server.createList('job', jobsCount, { createAllocations: true });
@@ -2087,6 +2087,7 @@ function testFacet(
});
test(`selecting multiple options in the ${label} facet results in a broader search`, async function (assert) {
faker.seed(2);
const selection = [];
await beforeEach();

View File

@@ -106,6 +106,30 @@ module('Acceptance | sentinel policies', function (hooks) {
.hasText('hard-mandatory');
});
test('Edit Sentinel Policy: Scope', async function (assert) {
const policy = server.db.sentinelPolicies.findBy(
(sp) => sp.name === 'host-volume-policy'
);
await click('[data-test-sentinel-policy-name="host-volume-policy"]');
assert.equal(
currentURL(),
`/administration/sentinel-policies/${policy.id}`
);
await click('[data-test-scope="submit-host-volume"]');
await click('button[data-test-save-policy]');
assert.dom('.flash-message.alert-success').exists();
await Administration.visitSentinelPolicies();
const policyRow = find(
'[data-test-sentinel-policy-name="host-volume-policy"]'
).closest('[data-test-sentinel-policy-row]');
assert.dom(policyRow).exists();
assert
.dom(policyRow.querySelector('[data-test-sentinel-policy-scope]'))
.hasText('submit-host-volume');
});
test('New Sentinel Policy from Scratch', async function (assert) {
await click('[data-test-create-sentinel-policy]');
assert.equal(currentURL(), '/administration/sentinel-policies/new');

View File

@@ -12,6 +12,7 @@ import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import Task from 'nomad-ui/tests/pages/allocations/task/detail';
import Layout from 'nomad-ui/tests/pages/layout';
import moment from 'moment';
import faker from 'faker';
let allocation;
let task;
@@ -213,6 +214,7 @@ module('Acceptance | task detail', function (hooks) {
});
test('when a task group has metadata, the metadata table is shown', async function (assert) {
faker.seed(2);
const job = server.create('job', {
createAllocations: false,
});