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

Nomad Services: job routes, model, and serializer updates #14226

Merged
merged 15 commits into from
Aug 26, 2022
Merged
5 changes: 5 additions & 0 deletions ui/app/adapters/service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Watchable from './watchable';
import classic from 'ember-classic-decorator';

@classic
export default class ServiceAdapter extends Watchable {}
7 changes: 5 additions & 2 deletions ui/app/adapters/watchable.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ export default class Watchable extends ApplicationAdapter {
reloadRelationship(
model,
relationshipName,
options = { watch: false, abortController: null }
options = { watch: false, abortController: null, replace: false }
) {
const { watch, abortController } = options;
const { watch, abortController, replace } = options;
const relationship = model.relationshipFor(relationshipName);
if (relationship.kind !== 'belongsTo' && relationship.kind !== 'hasMany') {
throw new Error(
Expand Down Expand Up @@ -185,6 +185,9 @@ export default class Watchable extends ApplicationAdapter {
modelClass,
json
);
if (replace) {
store.unloadAll(relationship.type);
}
store.push(normalizedData);
},
(error) => {
Expand Down
2 changes: 1 addition & 1 deletion ui/app/controllers/allocations/allocation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default class IndexController extends Controller.extend(Sortable) {
@computed('tasks.@each.services')
get taskServices() {
return this.get('tasks')
.map((t) => (t && t.get('services') || []).toArray())
.map((t) => ((t && t.get('services')) || []).toArray())
.flat()
.compact();
}
Expand Down
3 changes: 3 additions & 0 deletions ui/app/controllers/jobs/job/services.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Controller from '@ember/controller';

export default class JobsJobServicesController extends Controller {}
28 changes: 28 additions & 0 deletions ui/app/controllers/jobs/job/services/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Controller from '@ember/controller';
import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
import { alias } from '@ember/object/computed';
import { computed } from '@ember/object';

export default class JobsJobServicesIndexController extends Controller.extend(
WithNamespaceResetting
) {
@alias('model') job;

// Services, grouped by name, with aggregatable allocations.
@computed('job.services.@each.{name,allocation}', 'job.services.length')
get services() {
return this.job.services.reduce((m, n) => {
n.allocations = [];
let siblingServiceInstance = m.findBy('name', n.name);
if (!siblingServiceInstance) {
siblingServiceInstance = n;
m.push(n);
}
siblingServiceInstance.allocations = [
...(siblingServiceInstance.allocations || []),
n.allocation,
];
return m;
}, []);
}
}
1 change: 1 addition & 0 deletions ui/app/models/job.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export default class Job extends Model {
@hasMany('variables') variables;
@belongsTo('namespace') namespace;
@belongsTo('job-scale') scaleState;
@hasMany('services') services;

@hasMany('recommendation-summary') recommendationSummaries;

Expand Down
11 changes: 11 additions & 0 deletions ui/app/models/service-fragment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { attr } from '@ember-data/model';
import Fragment from 'ember-data-model-fragments/fragment';
import { fragment } from 'ember-data-model-fragments/attributes';

export default class Service extends Fragment {
@attr('string') name;
@attr('string') portLabel;
@attr() tags;
@attr('string') onUpdate;
@fragment('consul-connect') connect;
}
26 changes: 18 additions & 8 deletions ui/app/models/service.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { attr } from '@ember-data/model';
import Fragment from 'ember-data-model-fragments/fragment';
import { fragment } from 'ember-data-model-fragments/attributes';
// @ts-check
import { attr, belongsTo } from '@ember-data/model';
import Model from '@ember-data/model';
import { alias } from '@ember/object/computed';

export default class Service extends Fragment {
@attr('string') name;
@attr('string') portLabel;
export default class Service extends Model {
@belongsTo('allocation') allocation;
@belongsTo('job') job;
@belongsTo('node') node;

@attr('string') address;
@attr('number') createIndex;
@attr('string') datacenter;
@attr('number') modifyIndex;
@attr('string') namespace;
@attr('number') port;
@attr('string') serviceName;
@attr() tags;
@attr('string') onUpdate;
@fragment('consul-connect') connect;

@alias('serviceName') name;
}
2 changes: 1 addition & 1 deletion ui/app/models/task-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default class TaskGroup extends Fragment {

@fragmentArray('task') tasks;

@fragmentArray('service') services;
@fragmentArray('service-fragment') services;

@fragmentArray('volume-definition') volumes;

Expand Down
2 changes: 1 addition & 1 deletion ui/app/models/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class Task extends Fragment {
@attr('number') reservedCPU;
@attr('number') reservedDisk;
@attr('number') reservedEphemeralDisk;
@fragmentArray('service') services;
@fragmentArray('service-fragment') services;

@fragmentArray('volume-mount', { defaultValue: () => [] }) volumeMounts;

Expand Down
3 changes: 3 additions & 0 deletions ui/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ Router.map(function () {
this.route('evaluations');
this.route('allocations');
this.route('clients');
this.route('services', function () {
this.route('service', { path: '/:name' });
});
});
});

Expand Down
21 changes: 21 additions & 0 deletions ui/app/routes/jobs/job/services.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Route from '@ember/routing/route';
import WithWatchers from 'nomad-ui/mixins/with-watchers';
import { collect } from '@ember/object/computed';
import { watchRelationship } from 'nomad-ui/utils/properties/watch';

export default class JobsJobServicesRoute extends Route.extend(WithWatchers) {
model() {
const job = this.modelFor('jobs.job');
return job && job.get('services').then(() => job);
}

startWatchers(controller, model) {
if (model) {
controller.set('watchServices', this.watchServices.perform(model));
}
}

@watchRelationship('services', true) watchServices;

@collect('watchServices') watchers;
}
3 changes: 3 additions & 0 deletions ui/app/routes/jobs/job/services/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Route from '@ember/routing/route';

export default class JobsJobServicesIndexRoute extends Route {}
3 changes: 3 additions & 0 deletions ui/app/routes/jobs/job/services/service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Route from '@ember/routing/route';

export default class JobsJobServicesServiceRoute extends Route {}
5 changes: 5 additions & 0 deletions ui/app/serializers/job.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ export default class JobSerializer extends ApplicationSerializer {
related: buildURL(`${jobURL}/evaluations`, { namespace }),
},
},
services: {
links: {
related: buildURL(`${jobURL}/services`, { namespace }),
},
},
variables: {
links: {
related: buildURL(`/${apiNamespace}/vars`, {
Expand Down
11 changes: 11 additions & 0 deletions ui/app/serializers/service-fragment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ApplicationSerializer from './application';
import classic from 'ember-classic-decorator';

@classic
export default class ServiceFragmentSerializer extends ApplicationSerializer {
attrs = {
connect: 'Connect',
};

arrayNullOverrides = ['Tags'];
}
12 changes: 5 additions & 7 deletions ui/app/serializers/service.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import ApplicationSerializer from './application';
import classic from 'ember-classic-decorator';

@classic
export default class ServiceSerializer extends ApplicationSerializer {
attrs = {
connect: 'Connect',
};

arrayNullOverrides = ['Tags'];
normalize(typeHash, hash) {
hash.AllocationID = hash.AllocID; // TODO: keyForRelationship maybe?
hash.JobID = JSON.stringify([hash.JobID, hash.Namespace]);
return super.normalize(typeHash, hash);
}
}
16 changes: 16 additions & 0 deletions ui/app/templates/components/job-service-row.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<tr data-test-service={{@service.id}}>
<td>
{{@service.name}}
</td>
<td>
{{@service.node.name}}
</td>
<td>
{{#each @service.tags as |tag|}}
<span class="tag">{{tag}}</span>
{{/each}}
</td>
<td>
{{@service.allocations.length}} {{pluralize "allocation" @service.allocations.length}}
</td>
</tr>
9 changes: 9 additions & 0 deletions ui/app/templates/components/job-subnav.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,14 @@
</LinkTo>
</li>
{{/if}}
<li data-test-tab="services">
<LinkTo
@route="jobs.job.services"
@model={{@job}}
@activeClass="is-active"
>
Services
</LinkTo>
</li>
</ul>
</div>
5 changes: 5 additions & 0 deletions ui/app/templates/jobs/job/services.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{page-title "Job " @model.name " services"}}
<JobSubnav @job={{@model}} />
<section class="section">
{{outlet}}
</section>
29 changes: 29 additions & 0 deletions ui/app/templates/jobs/job/services/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{{#if this.services.length}}
<ListTable
@source={{this.services}}
@sortProperty={{this.sortProperty}}
@sortDescending={{this.sortDescending}}
@class="with-foot" as |t|
>
<t.head>
<t.sort-by @prop="name">name</t.sort-by>
<t.sort-by @prop="client" class="is-200px is-truncatable">Client</t.sort-by>
<th>Tags</th>
<t.sort-by @prop="numAllocs">Number of Alocations</t.sort-by>
</t.head>
<t.body as |row|>
<JobServiceRow @service={{row.model}} />
</t.body>
</ListTable>
{{else}}
<div class="boxed-section-body">
<div class="empty-message" data-test-empty-services-list>
<h3 class="empty-message-headline" data-test-empty-services-list-headline>
No Services
</h3>
<p class="empty-message-body">
No services running on {{this.job.name}}.
</p>
</div>
</div>
{{/if}}
2 changes: 2 additions & 0 deletions ui/app/templates/jobs/job/services/service.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{page-title "Service"}}
{{outlet}}
3 changes: 2 additions & 1 deletion ui/app/utils/properties/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function watchRecord(modelName) {
}).drop();
}

export function watchRelationship(relationshipName) {
export function watchRelationship(relationshipName, replace = false) {
return task(function* (model, throttle = 2000) {
assert(
'To watch a relationship, the adapter of the model provided to the watchRelationship task MUST extend Watchable',
Expand All @@ -54,6 +54,7 @@ export function watchRelationship(relationshipName) {
.reloadRelationship(model, relationshipName, {
watch: true,
abortController: controller,
replace,
}),
wait(throttle),
]);
Expand Down
1 change: 0 additions & 1 deletion ui/tests/acceptance/regions-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ module('Acceptance | regions (many)', function (hooks) {
const newRegion = server.db.regions[1].id;

await Allocation.visit({ id: server.db.allocations[0].id });
await this.pauseTest();

await selectChoose('[data-test-region-switcher-parent]', newRegion);

Expand Down
Empty file.
12 changes: 12 additions & 0 deletions ui/tests/unit/adapters/service-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Adapter | service', function (hooks) {
setupTest(hooks);

// Replace this with your real tests.
test('it exists', function (assert) {
let adapter = this.owner.lookup('adapter:service');
assert.ok(adapter);
});
});
12 changes: 12 additions & 0 deletions ui/tests/unit/controllers/jobs/job/services-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Controller | jobs/job/services', function (hooks) {
setupTest(hooks);

// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup('controller:jobs/job/services');
assert.ok(controller);
});
});
12 changes: 12 additions & 0 deletions ui/tests/unit/controllers/jobs/job/services/index-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Controller | jobs/job/services/index', function (hooks) {
setupTest(hooks);

// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup('controller:jobs/job/services/index');
assert.ok(controller);
});
});
13 changes: 13 additions & 0 deletions ui/tests/unit/models/service-fragment-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Model | service fragment', function (hooks) {
setupTest(hooks);

// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('service-fragment', {});
assert.ok(model);
});
});
11 changes: 11 additions & 0 deletions ui/tests/unit/routes/jobs/job/services/index-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Route | jobs/job/services/index', function (hooks) {
setupTest(hooks);

test('it exists', function (assert) {
let route = this.owner.lookup('route:jobs/job/services/index');
assert.ok(route);
});
});
11 changes: 11 additions & 0 deletions ui/tests/unit/routes/jobs/job/services/service-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Route | jobs/job/services/service', function (hooks) {
setupTest(hooks);

test('it exists', function (assert) {
let route = this.owner.lookup('route:jobs/job/services/service');
assert.ok(route);
});
});
Loading