github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/ui/tests/acceptance/task-detail-test.js (about)

     1  import { currentURL } from '@ember/test-helpers';
     2  import { module, test } from 'qunit';
     3  import { setupApplicationTest } from 'ember-qunit';
     4  import { setupMirage } from 'ember-cli-mirage/test-support';
     5  import Task from 'nomad-ui/tests/pages/allocations/task/detail';
     6  import moment from 'moment';
     7  
     8  let allocation;
     9  let task;
    10  
    11  module('Acceptance | task detail', function(hooks) {
    12    setupApplicationTest(hooks);
    13    setupMirage(hooks);
    14  
    15    hooks.beforeEach(async function() {
    16      server.create('agent');
    17      server.create('node');
    18      server.create('job', { createAllocations: false });
    19      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' });
    20      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
    21  
    22      await Task.visit({ id: allocation.id, name: task.name });
    23    });
    24  
    25    test('/allocation/:id/:task_name should name the task and list high-level task information', async function(assert) {
    26      assert.ok(Task.title.text.includes(task.name), 'Task name');
    27      assert.ok(Task.state.includes(task.state), 'Task state');
    28  
    29      assert.ok(
    30        Task.startedAt.includes(moment(task.startedAt).format("MMM DD, 'YY HH:mm:ss ZZ")),
    31        'Task started at'
    32      );
    33  
    34      const lifecycle = server.db.tasks.where({ name: task.name })[0].Lifecycle;
    35      const prestartString = lifecycle && lifecycle.Sidecar ? 'sidecar' : 'prestart';
    36      assert.equal(Task.lifecycle, lifecycle ? prestartString : 'main');
    37  
    38      assert.equal(document.title, `Task ${task.name} - Nomad`);
    39    });
    40  
    41    test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) {
    42      const { jobId, taskGroup } = allocation;
    43      const job = server.db.jobs.find(jobId);
    44  
    45      const shortId = allocation.id.split('-')[0];
    46  
    47      assert.equal(Task.breadcrumbFor('jobs.index').text, 'Jobs', 'Jobs is the first breadcrumb');
    48      assert.equal(
    49        Task.breadcrumbFor('jobs.job.index').text,
    50        job.name,
    51        'Job is the second breadcrumb'
    52      );
    53      assert.equal(
    54        Task.breadcrumbFor('jobs.job.task-group').text,
    55        taskGroup,
    56        'Task Group is the third breadcrumb'
    57      );
    58      assert.equal(
    59        Task.breadcrumbFor('allocations.allocation').text,
    60        shortId,
    61        'Allocation short id is the fourth breadcrumb'
    62      );
    63      assert.equal(
    64        Task.breadcrumbFor('allocations.allocation.task').text,
    65        task.name,
    66        'Task name is the fifth breadcrumb'
    67      );
    68  
    69      await Task.breadcrumbFor('jobs.index').visit();
    70      assert.equal(currentURL(), '/jobs', 'Jobs breadcrumb links correctly');
    71  
    72      await Task.visit({ id: allocation.id, name: task.name });
    73      await Task.breadcrumbFor('jobs.job.index').visit();
    74      assert.equal(currentURL(), `/jobs/${job.id}`, 'Job breadcrumb links correctly');
    75  
    76      await Task.visit({ id: allocation.id, name: task.name });
    77      await Task.breadcrumbFor('jobs.job.task-group').visit();
    78      assert.equal(
    79        currentURL(),
    80        `/jobs/${job.id}/${taskGroup}`,
    81        'Task Group breadcrumb links correctly'
    82      );
    83  
    84      await Task.visit({ id: allocation.id, name: task.name });
    85      await Task.breadcrumbFor('allocations.allocation').visit();
    86      assert.equal(
    87        currentURL(),
    88        `/allocations/${allocation.id}`,
    89        'Allocations breadcrumb links correctly'
    90      );
    91    });
    92  
    93    test('/allocation/:id/:task_name should include resource utilization graphs', async function(assert) {
    94      assert.equal(Task.resourceCharts.length, 2, 'Two resource utilization graphs');
    95      assert.equal(Task.resourceCharts.objectAt(0).name, 'CPU', 'First chart is CPU');
    96      assert.equal(Task.resourceCharts.objectAt(1).name, 'Memory', 'Second chart is Memory');
    97    });
    98  
    99    test('/allocation/:id/:task_name lists related prestart tasks for a main task when they exist', async function(assert) {
   100      const job = server.create('job', {
   101        groupsCount: 2,
   102        groupTaskCount: 3,
   103        createAllocations: false,
   104        status: 'running',
   105      });
   106  
   107      job.task_group_ids.forEach(taskGroupId => {
   108        server.create('allocation', {
   109          jobId: job.id,
   110          taskGroup: server.db.taskGroups.find(taskGroupId).name,
   111          forceRunningClientStatus: true,
   112        });
   113      });
   114  
   115      const taskGroup = job.task_groups.models[0];
   116      const [mainTask, sidecarTask, prestartTask] = taskGroup.tasks.models;
   117  
   118      mainTask.attrs.Lifecycle = null;
   119      mainTask.save();
   120  
   121      sidecarTask.attrs.Lifecycle = { Sidecar: true, Hook: 'prestart' };
   122      sidecarTask.save();
   123  
   124      prestartTask.attrs.Lifecycle = { Sidecar: false, Hook: 'prestart' };
   125      prestartTask.save();
   126  
   127      taskGroup.save();
   128  
   129      const noPrestartTasksTaskGroup = job.task_groups.models[1];
   130      noPrestartTasksTaskGroup.tasks.models.forEach(task => {
   131        task.attrs.Lifecycle = null;
   132        task.save();
   133      });
   134  
   135      const mainTaskState = server.schema.taskStates.findBy({ name: mainTask.name });
   136      const sidecarTaskState = server.schema.taskStates.findBy({ name: sidecarTask.name });
   137      const prestartTaskState = server.schema.taskStates.findBy({ name: prestartTask.name });
   138  
   139      prestartTaskState.attrs.state = 'running';
   140      prestartTaskState.attrs.finishedAt = null;
   141      prestartTaskState.save();
   142  
   143      await Task.visit({ id: mainTaskState.allocationId, name: mainTask.name });
   144  
   145      assert.ok(Task.hasPrestartTasks);
   146      assert.equal(Task.prestartTasks.length, 2);
   147  
   148      Task.prestartTasks[0].as(SidecarTask => {
   149        assert.equal(SidecarTask.name, sidecarTask.name);
   150        assert.equal(SidecarTask.state, sidecarTaskState.state);
   151        assert.equal(SidecarTask.lifecycle, 'sidecar');
   152        assert.notOk(SidecarTask.isBlocking);
   153      });
   154  
   155      Task.prestartTasks[1].as(PrestartTask => {
   156        assert.equal(PrestartTask.name, prestartTask.name);
   157        assert.equal(PrestartTask.state, prestartTaskState.state);
   158        assert.equal(PrestartTask.lifecycle, 'prestart');
   159        assert.ok(PrestartTask.isBlocking);
   160      });
   161  
   162      await Task.visit({ id: sidecarTaskState.allocationId, name: sidecarTask.name });
   163  
   164      assert.notOk(Task.hasPrestartTasks);
   165  
   166      const noPrestartTasksTask = noPrestartTasksTaskGroup.tasks.models[0];
   167      const noPrestartTasksTaskState = server.db.taskStates.findBy({
   168        name: noPrestartTasksTask.name,
   169      });
   170  
   171      await Task.visit({
   172        id: noPrestartTasksTaskState.allocationId,
   173        name: noPrestartTasksTaskState.name,
   174      });
   175  
   176      assert.notOk(Task.hasPrestartTasks);
   177    });
   178  
   179    test('the addresses table lists all reserved and dynamic ports', async function(assert) {
   180      const taskResources = allocation.taskResourceIds
   181        .map(id => server.db.taskResources.find(id))
   182        .find(resources => resources.name === task.name);
   183      const reservedPorts = taskResources.resources.Networks[0].ReservedPorts;
   184      const dynamicPorts = taskResources.resources.Networks[0].DynamicPorts;
   185      const addresses = reservedPorts.concat(dynamicPorts);
   186  
   187      assert.equal(Task.addresses.length, addresses.length, 'All addresses are listed');
   188    });
   189  
   190    test('each address row shows the label and value of the address', async function(assert) {
   191      const taskResources = allocation.taskResourceIds
   192        .map(id => server.db.taskResources.find(id))
   193        .findBy('name', task.name);
   194      const networkAddress = taskResources.resources.Networks[0].IP;
   195      const reservedPorts = taskResources.resources.Networks[0].ReservedPorts;
   196      const dynamicPorts = taskResources.resources.Networks[0].DynamicPorts;
   197      const address = reservedPorts.concat(dynamicPorts).sortBy('Label')[0];
   198  
   199      const addressRow = Task.addresses.objectAt(0);
   200      assert.equal(
   201        addressRow.isDynamic,
   202        reservedPorts.includes(address) ? 'No' : 'Yes',
   203        'Dynamic port is denoted as such'
   204      );
   205      assert.equal(addressRow.name, address.Label, 'Label');
   206      assert.equal(addressRow.address, `${networkAddress}:${address.Value}`, 'Value');
   207    });
   208  
   209    test('the events table lists all recent events', async function(assert) {
   210      const events = server.db.taskEvents.where({ taskStateId: task.id });
   211  
   212      assert.equal(Task.events.length, events.length, `Lists ${events.length} events`);
   213    });
   214  
   215    test('when a task has volumes, the volumes table is shown', async function(assert) {
   216      const taskGroup = server.schema.taskGroups.where({
   217        jobId: allocation.jobId,
   218        name: allocation.taskGroup,
   219      }).models[0];
   220  
   221      const jobTask = taskGroup.tasks.models.find(m => m.name === task.name);
   222  
   223      assert.ok(Task.hasVolumes);
   224      assert.equal(Task.volumes.length, jobTask.volumeMounts.length);
   225    });
   226  
   227    test('when a task does not have volumes, the volumes table is not shown', async function(assert) {
   228      const job = server.create('job', { createAllocations: false, noHostVolumes: true });
   229      allocation = server.create('allocation', { jobId: job.id, clientStatus: 'running' });
   230      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   231  
   232      await Task.visit({ id: allocation.id, name: task.name });
   233      assert.notOk(Task.hasVolumes);
   234    });
   235  
   236    test('each volume in the volumes table shows information about the volume', async function(assert) {
   237      const taskGroup = server.schema.taskGroups.where({
   238        jobId: allocation.jobId,
   239        name: allocation.taskGroup,
   240      }).models[0];
   241  
   242      const jobTask = taskGroup.tasks.models.find(m => m.name === task.name);
   243      const volume = jobTask.volumeMounts[0];
   244  
   245      Task.volumes[0].as(volumeRow => {
   246        assert.equal(volumeRow.name, volume.Volume);
   247        assert.equal(volumeRow.destination, volume.Destination);
   248        assert.equal(volumeRow.permissions, volume.ReadOnly ? 'Read' : 'Read/Write');
   249        assert.equal(volumeRow.clientSource, taskGroup.volumes[volume.Volume].Source);
   250      });
   251    });
   252  
   253    test('each recent event should list the time, type, and description of the event', async function(assert) {
   254      const event = server.db.taskEvents.where({ taskStateId: task.id })[0];
   255      const recentEvent = Task.events.objectAt(Task.events.length - 1);
   256  
   257      assert.equal(
   258        recentEvent.time,
   259        moment(event.time / 1000000).format("MMM DD, 'YY HH:mm:ss ZZ"),
   260        'Event timestamp'
   261      );
   262      assert.equal(recentEvent.type, event.type, 'Event type');
   263      assert.equal(recentEvent.message, event.displayMessage, 'Event message');
   264    });
   265  
   266    test('when the allocation is not found, the application errors', async function(assert) {
   267      await Task.visit({ id: 'not-a-real-allocation', name: task.name });
   268  
   269      assert.equal(
   270        server.pretender.handledRequests
   271          .filter(request => !request.url.includes('policy'))
   272          .findBy('status', 404).url,
   273        '/v1/allocation/not-a-real-allocation',
   274        'A request to the nonexistent allocation is made'
   275      );
   276      assert.equal(
   277        currentURL(),
   278        `/allocations/not-a-real-allocation/${task.name}`,
   279        'The URL persists'
   280      );
   281      assert.ok(Task.error.isPresent, 'Error message is shown');
   282      assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
   283    });
   284  
   285    test('when the allocation is found but the task is not, the application errors', async function(assert) {
   286      await Task.visit({ id: allocation.id, name: 'not-a-real-task-name' });
   287  
   288      assert.ok(
   289        server.pretender.handledRequests
   290          .filterBy('status', 200)
   291          .mapBy('url')
   292          .includes(`/v1/allocation/${allocation.id}`),
   293        'A request to the allocation is made successfully'
   294      );
   295      assert.equal(
   296        currentURL(),
   297        `/allocations/${allocation.id}/not-a-real-task-name`,
   298        'The URL persists'
   299      );
   300      assert.ok(Task.error.isPresent, 'Error message is shown');
   301      assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
   302    });
   303  
   304    test('task can be restarted', async function(assert) {
   305      await Task.restart.idle();
   306      await Task.restart.confirm();
   307  
   308      const request = server.pretender.handledRequests.findBy('method', 'PUT');
   309      assert.equal(
   310        request.url,
   311        `/v1/client/allocation/${allocation.id}/restart`,
   312        'Restart request is made for the allocation'
   313      );
   314  
   315      assert.deepEqual(
   316        JSON.parse(request.requestBody),
   317        { TaskName: task.name },
   318        'Restart request is made for the correct task'
   319      );
   320    });
   321  
   322    test('when task restart fails, an error message is shown', async function(assert) {
   323      server.pretender.put('/v1/client/allocation/:id/restart', () => [403, {}, '']);
   324  
   325      await Task.restart.idle();
   326      await Task.restart.confirm();
   327  
   328      assert.ok(Task.inlineError.isShown, 'Inline error is shown');
   329      assert.ok(Task.inlineError.title.includes('Could Not Restart Task'), 'Title is descriptive');
   330      assert.ok(
   331        /ACL token.+?allocation lifecycle/.test(Task.inlineError.message),
   332        'Message mentions ACLs and the appropriate permission'
   333      );
   334  
   335      await Task.inlineError.dismiss();
   336  
   337      assert.notOk(Task.inlineError.isShown, 'Inline error is no longer shown');
   338    });
   339  
   340    test('exec button is present', async function(assert) {
   341      assert.ok(Task.execButton.isPresent);
   342    });
   343  });
   344  
   345  module('Acceptance | task detail (no addresses)', function(hooks) {
   346    setupApplicationTest(hooks);
   347    setupMirage(hooks);
   348  
   349    hooks.beforeEach(async function() {
   350      server.create('agent');
   351      server.create('node');
   352      server.create('job');
   353      allocation = server.create('allocation', 'withoutTaskWithPorts', { clientStatus: 'running' });
   354      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   355  
   356      await Task.visit({ id: allocation.id, name: task.name });
   357    });
   358  
   359    test('when the task has no addresses, the addresses table is not shown', async function(assert) {
   360      assert.notOk(Task.hasAddresses, 'No addresses table');
   361    });
   362  });
   363  
   364  module('Acceptance | task detail (different namespace)', function(hooks) {
   365    setupApplicationTest(hooks);
   366    setupMirage(hooks);
   367  
   368    hooks.beforeEach(async function() {
   369      server.create('agent');
   370      server.create('node');
   371      server.create('namespace');
   372      server.create('namespace', { id: 'other-namespace' });
   373      server.create('job', { createAllocations: false, namespaceId: 'other-namespace' });
   374      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' });
   375      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   376  
   377      await Task.visit({ id: allocation.id, name: task.name });
   378    });
   379  
   380    test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) {
   381      const { jobId, taskGroup } = allocation;
   382      const job = server.db.jobs.find(jobId);
   383  
   384      await Task.breadcrumbFor('jobs.index').visit();
   385      assert.equal(
   386        currentURL(),
   387        '/jobs?namespace=other-namespace',
   388        'Jobs breadcrumb links correctly'
   389      );
   390  
   391      await Task.visit({ id: allocation.id, name: task.name });
   392      await Task.breadcrumbFor('jobs.job.index').visit();
   393      assert.equal(
   394        currentURL(),
   395        `/jobs/${job.id}?namespace=other-namespace`,
   396        'Job breadcrumb links correctly'
   397      );
   398  
   399      await Task.visit({ id: allocation.id, name: task.name });
   400      await Task.breadcrumbFor('jobs.job.task-group').visit();
   401      assert.equal(
   402        currentURL(),
   403        `/jobs/${job.id}/${taskGroup}?namespace=other-namespace`,
   404        'Task Group breadcrumb links correctly'
   405      );
   406  
   407      await Task.visit({ id: allocation.id, name: task.name });
   408      await Task.breadcrumbFor('allocations.allocation').visit();
   409      assert.equal(
   410        currentURL(),
   411        `/allocations/${allocation.id}`,
   412        'Allocations breadcrumb links correctly'
   413      );
   414    });
   415  });
   416  
   417  module('Acceptance | task detail (not running)', function(hooks) {
   418    setupApplicationTest(hooks);
   419    setupMirage(hooks);
   420  
   421    hooks.beforeEach(async function() {
   422      server.create('agent');
   423      server.create('node');
   424      server.create('namespace');
   425      server.create('namespace', { id: 'other-namespace' });
   426      server.create('job', { createAllocations: false, namespaceId: 'other-namespace' });
   427      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'complete' });
   428      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   429  
   430      await Task.visit({ id: allocation.id, name: task.name });
   431    });
   432  
   433    test('when the allocation for a task is not running, the resource utilization graphs are replaced by an empty message', async function(assert) {
   434      assert.equal(Task.resourceCharts.length, 0, 'No resource charts');
   435      assert.equal(Task.resourceEmptyMessage, "Task isn't running", 'Empty message is appropriate');
   436    });
   437  
   438    test('exec button is absent', async function(assert) {
   439      assert.notOk(Task.execButton.isPresent);
   440    });
   441  });
   442  
   443  module('Acceptance | proxy task detail', function(hooks) {
   444    setupApplicationTest(hooks);
   445    setupMirage(hooks);
   446  
   447    hooks.beforeEach(async function() {
   448      server.create('agent');
   449      server.create('node');
   450      server.create('job', { createAllocations: false });
   451      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' });
   452  
   453      const taskState = allocation.task_states.models[0];
   454      const task = server.schema.tasks.findBy({ name: taskState.name });
   455      task.update('kind', 'connect-proxy:task');
   456      task.save();
   457  
   458      await Task.visit({ id: allocation.id, name: taskState.name });
   459    });
   460  
   461    test('a proxy tag is shown', async function(assert) {
   462      assert.ok(Task.title.proxyTag.isPresent);
   463    });
   464  });