github.com/aminovpavel/nomad@v0.11.8/ui/tests/acceptance/task-group-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 { formatBytes } from 'nomad-ui/helpers/format-bytes';
     6  import TaskGroup from 'nomad-ui/tests/pages/jobs/job/task-group';
     7  import pageSizeSelect from './behaviors/page-size-select';
     8  import moment from 'moment';
     9  
    10  let job;
    11  let taskGroup;
    12  let tasks;
    13  let allocations;
    14  
    15  const sum = (total, n) => total + n;
    16  
    17  module('Acceptance | task group detail', function(hooks) {
    18    setupApplicationTest(hooks);
    19    setupMirage(hooks);
    20  
    21    hooks.beforeEach(async function() {
    22      server.create('agent');
    23      server.create('node', 'forceIPv4');
    24  
    25      job = server.create('job', {
    26        groupsCount: 2,
    27        createAllocations: false,
    28      });
    29  
    30      const taskGroups = server.db.taskGroups.where({ jobId: job.id });
    31      taskGroup = taskGroups[0];
    32  
    33      tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
    34  
    35      server.create('node', 'forceIPv4');
    36  
    37      allocations = server.createList('allocation', 2, {
    38        jobId: job.id,
    39        taskGroup: taskGroup.name,
    40        clientStatus: 'running',
    41      });
    42  
    43      // Allocations associated to a different task group on the job to
    44      // assert that they aren't showing up in on this page in error.
    45      server.createList('allocation', 3, {
    46        jobId: job.id,
    47        taskGroup: taskGroups[1].name,
    48        clientStatus: 'running',
    49      });
    50  
    51      // Set a static name to make the search test deterministic
    52      server.db.allocations.forEach(alloc => {
    53        alloc.name = 'aaaaa';
    54      });
    55  
    56      // Mark the first alloc as rescheduled
    57      allocations[0].update({
    58        nextAllocation: allocations[1].id,
    59      });
    60      allocations[1].update({
    61        previousAllocation: allocations[0].id,
    62      });
    63  
    64      window.localStorage.clear();
    65    });
    66  
    67    test('/jobs/:id/:task-group should list high-level metrics for the allocation', async function(assert) {
    68      const totalCPU = tasks.mapBy('Resources.CPU').reduce(sum, 0);
    69      const totalMemory = tasks.mapBy('Resources.MemoryMB').reduce(sum, 0);
    70      const totalDisk = taskGroup.ephemeralDisk.SizeMB;
    71  
    72      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
    73  
    74      assert.equal(TaskGroup.tasksCount, `# Tasks ${tasks.length}`, '# Tasks');
    75      assert.equal(
    76        TaskGroup.cpu,
    77        `Reserved CPU ${totalCPU} MHz`,
    78        'Aggregated CPU reservation for all tasks'
    79      );
    80      assert.equal(
    81        TaskGroup.mem,
    82        `Reserved Memory ${totalMemory} MiB`,
    83        'Aggregated Memory reservation for all tasks'
    84      );
    85      assert.equal(
    86        TaskGroup.disk,
    87        `Reserved Disk ${totalDisk} MiB`,
    88        'Aggregated Disk reservation for all tasks'
    89      );
    90  
    91      assert.equal(document.title, `Task group ${taskGroup.name} - Job ${job.name} - Nomad`);
    92    });
    93  
    94    test('/jobs/:id/:task-group should have breadcrumbs for job and jobs', async function(assert) {
    95      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
    96  
    97      assert.equal(TaskGroup.breadcrumbFor('jobs.index').text, 'Jobs', 'First breadcrumb says jobs');
    98      assert.equal(
    99        TaskGroup.breadcrumbFor('jobs.job.index').text,
   100        job.name,
   101        'Second breadcrumb says the job name'
   102      );
   103      assert.equal(
   104        TaskGroup.breadcrumbFor('jobs.job.task-group').text,
   105        taskGroup.name,
   106        'Third breadcrumb says the job name'
   107      );
   108    });
   109  
   110    test('/jobs/:id/:task-group first breadcrumb should link to jobs', async function(assert) {
   111      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   112  
   113      await TaskGroup.breadcrumbFor('jobs.index').visit();
   114      assert.equal(currentURL(), '/jobs', 'First breadcrumb links back to jobs');
   115    });
   116  
   117    test('/jobs/:id/:task-group second breadcrumb should link to the job for the task group', async function(assert) {
   118      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   119  
   120      await TaskGroup.breadcrumbFor('jobs.job.index').visit();
   121      assert.equal(
   122        currentURL(),
   123        `/jobs/${job.id}`,
   124        'Second breadcrumb links back to the job for the task group'
   125      );
   126    });
   127  
   128    test('/jobs/:id/:task-group should list one page of allocations for the task group', async function(assert) {
   129      server.createList('allocation', TaskGroup.pageSize, {
   130        jobId: job.id,
   131        taskGroup: taskGroup.name,
   132        clientStatus: 'running',
   133      });
   134  
   135      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   136  
   137      assert.ok(
   138        server.db.allocations.where({ jobId: job.id }).length > TaskGroup.pageSize,
   139        'There are enough allocations to invoke pagination'
   140      );
   141  
   142      assert.equal(
   143        TaskGroup.allocations.length,
   144        TaskGroup.pageSize,
   145        'All allocations for the task group'
   146      );
   147    });
   148  
   149    test('each allocation should show basic information about the allocation', async function(assert) {
   150      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   151  
   152      const allocation = allocations.sortBy('modifyIndex').reverse()[0];
   153      const allocationRow = TaskGroup.allocations.objectAt(0);
   154  
   155      assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'Allocation short id');
   156      assert.equal(
   157        allocationRow.createTime,
   158        moment(allocation.createTime / 1000000).format('MMM DD HH:mm:ss ZZ'),
   159        'Allocation create time'
   160      );
   161      assert.equal(
   162        allocationRow.modifyTime,
   163        moment(allocation.modifyTime / 1000000).fromNow(),
   164        'Allocation modify time'
   165      );
   166      assert.equal(allocationRow.status, allocation.clientStatus, 'Client status');
   167      assert.equal(allocationRow.jobVersion, allocation.jobVersion, 'Job Version');
   168      assert.equal(
   169        allocationRow.client,
   170        server.db.nodes.find(allocation.nodeId).id.split('-')[0],
   171        'Node ID'
   172      );
   173      assert.equal(
   174        allocationRow.volume,
   175        Object.keys(taskGroup.volumes).length ? 'Yes' : '',
   176        'Volumes'
   177      );
   178  
   179      await allocationRow.visitClient();
   180  
   181      assert.equal(currentURL(), `/clients/${allocation.nodeId}`, 'Node links to node page');
   182    });
   183  
   184    test('each allocation should show stats about the allocation', async function(assert) {
   185      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   186  
   187      const allocation = allocations.sortBy('name')[0];
   188      const allocationRow = TaskGroup.allocations.objectAt(0);
   189  
   190      const allocStats = server.db.clientAllocationStats.find(allocation.id);
   191      const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
   192  
   193      const cpuUsed = tasks.reduce((sum, task) => sum + task.Resources.CPU, 0);
   194      const memoryUsed = tasks.reduce((sum, task) => sum + task.Resources.MemoryMB, 0);
   195  
   196      assert.equal(
   197        allocationRow.cpu,
   198        Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed,
   199        'CPU %'
   200      );
   201  
   202      assert.equal(
   203        allocationRow.cpuTooltip,
   204        `${Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks)} / ${cpuUsed} MHz`,
   205        'Detailed CPU information is in a tooltip'
   206      );
   207  
   208      assert.equal(
   209        allocationRow.mem,
   210        allocStats.resourceUsage.MemoryStats.RSS / 1024 / 1024 / memoryUsed,
   211        'Memory used'
   212      );
   213  
   214      assert.equal(
   215        allocationRow.memTooltip,
   216        `${formatBytes([allocStats.resourceUsage.MemoryStats.RSS])} / ${memoryUsed} MiB`,
   217        'Detailed memory information is in a tooltip'
   218      );
   219    });
   220  
   221    test('when the allocation search has no matches, there is an empty message', async function(assert) {
   222      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   223  
   224      await TaskGroup.search('zzzzzz');
   225  
   226      assert.ok(TaskGroup.isEmpty, 'Empty state is shown');
   227      assert.equal(
   228        TaskGroup.emptyState.headline,
   229        'No Matches',
   230        'Empty state has an appropriate message'
   231      );
   232    });
   233  
   234    test('when the allocation has reschedule events, the allocation row is denoted with an icon', async function(assert) {
   235      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   236  
   237      const rescheduleRow = TaskGroup.allocationFor(allocations[0].id);
   238      const normalRow = TaskGroup.allocationFor(allocations[1].id);
   239  
   240      assert.ok(rescheduleRow.rescheduled, 'Reschedule row has a reschedule icon');
   241      assert.notOk(normalRow.rescheduled, 'Normal row has no reschedule icon');
   242    });
   243  
   244    test('/jobs/:id/:task-group should present task lifecycles', async function(assert) {
   245      job = server.create('job', {
   246        groupsCount: 2,
   247        groupTaskCount: 3,
   248      });
   249  
   250      const taskGroups = server.db.taskGroups.where({ jobId: job.id });
   251      taskGroup = taskGroups[0];
   252  
   253      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   254  
   255      assert.ok(TaskGroup.lifecycleChart.isPresent);
   256      assert.equal(TaskGroup.lifecycleChart.title, 'Task Lifecycle Configuration');
   257  
   258      tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
   259      const taskNames = tasks.mapBy('name');
   260  
   261      // This is thoroughly tested in allocation detail tests, so this mostly checks what’s different
   262  
   263      assert.equal(TaskGroup.lifecycleChart.tasks.length, 3);
   264  
   265      TaskGroup.lifecycleChart.tasks.forEach(Task => {
   266        assert.ok(taskNames.includes(Task.name));
   267        assert.notOk(Task.isActive);
   268        assert.notOk(Task.isFinished);
   269      });
   270    });
   271  
   272    test('when the task group depends on volumes, the volumes table is shown', async function(assert) {
   273      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   274  
   275      assert.ok(TaskGroup.hasVolumes);
   276      assert.equal(TaskGroup.volumes.length, Object.keys(taskGroup.volumes).length);
   277    });
   278  
   279    test('when the task group does not depend on volumes, the volumes table is not shown', async function(assert) {
   280      job = server.create('job', { noHostVolumes: true, shallow: true });
   281      taskGroup = server.db.taskGroups.where({ jobId: job.id })[0];
   282  
   283      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   284  
   285      assert.notOk(TaskGroup.hasVolumes);
   286    });
   287  
   288    test('each row in the volumes table lists information about the volume', async function(assert) {
   289      await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   290  
   291      TaskGroup.volumes[0].as(volumeRow => {
   292        const volume = taskGroup.volumes[volumeRow.name];
   293        assert.equal(volumeRow.name, volume.Name);
   294        assert.equal(volumeRow.type, volume.Type);
   295        assert.equal(volumeRow.source, volume.Source);
   296        assert.equal(volumeRow.permissions, volume.ReadOnly ? 'Read' : 'Read/Write');
   297      });
   298    });
   299  
   300    test('when the job for the task group is not found, an error message is shown, but the URL persists', async function(assert) {
   301      await TaskGroup.visit({ id: 'not-a-real-job', name: 'not-a-real-task-group' });
   302  
   303      assert.equal(
   304        server.pretender.handledRequests
   305          .filter(request => !request.url.includes('policy'))
   306          .findBy('status', 404).url,
   307        '/v1/job/not-a-real-job',
   308        'A request to the nonexistent job is made'
   309      );
   310      assert.equal(currentURL(), '/jobs/not-a-real-job/not-a-real-task-group', 'The URL persists');
   311      assert.ok(TaskGroup.error.isPresent, 'Error message is shown');
   312      assert.equal(TaskGroup.error.title, 'Not Found', 'Error message is for 404');
   313    });
   314  
   315    test('when the task group is not found on the job, an error message is shown, but the URL persists', async function(assert) {
   316      await TaskGroup.visit({ id: job.id, name: 'not-a-real-task-group' });
   317  
   318      assert.ok(
   319        server.pretender.handledRequests
   320          .filterBy('status', 200)
   321          .mapBy('url')
   322          .includes(`/v1/job/${job.id}`),
   323        'A request to the job is made and succeeds'
   324      );
   325      assert.equal(currentURL(), `/jobs/${job.id}/not-a-real-task-group`, 'The URL persists');
   326      assert.ok(TaskGroup.error.isPresent, 'Error message is shown');
   327      assert.equal(TaskGroup.error.title, 'Not Found', 'Error message is for 404');
   328    });
   329  
   330    pageSizeSelect({
   331      resourceName: 'allocation',
   332      pageObject: TaskGroup,
   333      pageObjectList: TaskGroup.allocations,
   334      async setup() {
   335        server.createList('allocation', TaskGroup.pageSize, {
   336          jobId: job.id,
   337          taskGroup: taskGroup.name,
   338          clientStatus: 'running',
   339        });
   340  
   341        await TaskGroup.visit({ id: job.id, name: taskGroup.name });
   342      },
   343    });
   344  });