Skip to content

Commit

Permalink
[bugfix, ui] Allow running jobs from a namespace-limited token (#13659)…
Browse files Browse the repository at this point in the history
… (#13687)

* Allow running jobs from a namespace-limited token

* qpNamespace cleanup

* Looks like parse can deal with a * namespace

* A little diff cleanup

* Defensive destructuring

* Removing accidental friendly-fire on can-scale

* Testfix: Job run buttons from jobs index

* Testfix: activeRegion job adapter string

* Testfix: unit tests for job abilities correctly reflect the any-namespace rule

* Testfix: job editor test looks for requests with namespace applied on plan

Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com>
  • Loading branch information
hc-github-team-nomad-core and philrenaud committed Jul 11, 2022
1 parent 0fbe65c commit 2a9efef
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 117 deletions.
29 changes: 26 additions & 3 deletions ui/app/abilities/job.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import AbstractAbility from './abstract';
import { computed } from '@ember/object';
import { computed, get } from '@ember/object';
import { or } from '@ember/object/computed';

export default class Job extends AbstractAbility {
Expand All @@ -9,7 +9,7 @@ export default class Job extends AbstractAbility {
@or(
'bypassAuthorization',
'selfTokenIsManagement',
'policiesSupportRunning',
'specificNamespaceSupportsRunning',
'policiesSupportScaling'
)
canScale;
Expand All @@ -23,8 +23,31 @@ export default class Job extends AbstractAbility {
@or('bypassAuthorization', 'selfTokenIsManagement', 'policiesSupportDispatching')
canDispatch;

@computed('rulesForNamespace.@each.capabilities')
policyNamespacesIncludePermissions(policies = [], permissions = []) {
// For each policy record, extract all policies of all namespaces
const allNamespacePolicies = policies
.toArray()
.map((policy) => get(policy, 'rulesJSON.Namespaces'))
.flat()
.map((namespace = {}) => {
return namespace.Capabilities;
})
.flat()
.compact();

// Check for requested permissions
return allNamespacePolicies.some((policy) => {
return permissions.includes(policy);
});
}

@computed('token.selfTokenPolicies.[]')
get policiesSupportRunning() {
return this.policyNamespacesIncludePermissions(this.token.selfTokenPolicies, ['submit-job']);
}

@computed('rulesForNamespace.@each.capabilities')
get specificNamespaceSupportsRunning() {
return this.namespaceIncludesCapability('submit-job');
}

Expand Down
4 changes: 2 additions & 2 deletions ui/app/adapters/job.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default class JobAdapter extends WatchableNamespaceIDs {
}

parse(spec) {
const url = addToPath(this.urlForFindAll('job'), '/parse');
const url = addToPath(this.urlForFindAll('job'), '/parse?namespace=*');
return this.ajax(url, 'POST', {
data: {
JobHCL: spec,
Expand All @@ -44,7 +44,7 @@ export default class JobAdapter extends WatchableNamespaceIDs {
Job: job.get('_newDefinitionJSON'),
Diff: true,
},
}).then(json => {
}).then((json) => {
json.ID = jobId;
store.pushPayload('job-plan', { jobPlans: [json] });
return store.peekRecord('job-plan', jobId);
Expand Down
8 changes: 4 additions & 4 deletions ui/app/components/job-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ export default class JobEditor extends Component {
return this.planOutput ? 'plan' : 'editor';
}

@(task(function*() {
@(task(function* () {
this.reset();

try {
yield this.job.parse();
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not parse input';
const error = messageFromAdapterError(err, 'parse jobs') || 'Could not parse input';
this.set('parseError', error);
this.scrollToError();
return;
Expand All @@ -61,14 +61,14 @@ export default class JobEditor extends Component {
const plan = yield this.job.plan();
this.set('planOutput', plan);
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not plan job';
const error = messageFromAdapterError(err, 'plan jobs') || 'Could not plan job';
this.set('planError', error);
this.scrollToError();
}
}).drop())
plan;

@task(function*() {
@task(function* () {
try {
if (this.context === 'new') {
yield this.job.run();
Expand Down
Loading

0 comments on commit 2a9efef

Please sign in to comment.