github.com/zhizhiboom/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/ui/tests/acceptance/job-deployments-test.js (about) 1 import { get } from '@ember/object'; 2 import { test } from 'qunit'; 3 import moment from 'moment'; 4 import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; 5 import Deployments from 'nomad-ui/tests/pages/jobs/job/deployments'; 6 7 const sum = (list, key, getter = a => a) => 8 list.reduce((sum, item) => sum + getter(get(item, key)), 0); 9 10 let job; 11 let deployments; 12 let sortedDeployments; 13 14 moduleForAcceptance('Acceptance | job deployments', { 15 beforeEach() { 16 server.create('node'); 17 job = server.create('job'); 18 deployments = server.schema.deployments.where({ jobId: job.id }); 19 sortedDeployments = deployments.sort((a, b) => { 20 const aVersion = server.db.jobVersions.findBy({ jobId: a.jobId, version: a.versionNumber }); 21 const bVersion = server.db.jobVersions.findBy({ jobId: b.jobId, version: b.versionNumber }); 22 if (aVersion.submitTime < bVersion.submitTime) { 23 return 1; 24 } else if (aVersion.submitTime > bVersion.submitTime) { 25 return -1; 26 } 27 return 0; 28 }); 29 }, 30 }); 31 32 test('/jobs/:id/deployments should list all job deployments', function(assert) { 33 Deployments.visit({ id: job.id }); 34 35 andThen(() => { 36 assert.ok( 37 Deployments.deployments.length, 38 deployments.length, 39 'Each deployment gets a row in the timeline' 40 ); 41 }); 42 }); 43 44 test('each deployment mentions the deployment shortId, status, version, and time since it was submitted', function(assert) { 45 Deployments.visit({ id: job.id }); 46 47 andThen(() => { 48 const deployment = sortedDeployments.models[0]; 49 const version = server.db.jobVersions.findBy({ 50 jobId: deployment.jobId, 51 version: deployment.versionNumber, 52 }); 53 const deploymentRow = Deployments.deployments.objectAt(0); 54 55 assert.ok(deploymentRow.text.includes(deployment.id.split('-')[0]), 'Short ID'); 56 assert.equal(deploymentRow.status, deployment.status, 'Status'); 57 assert.ok( 58 deploymentRow.statusClass.includes(classForStatus(deployment.status)), 59 'Status Class' 60 ); 61 assert.ok(deploymentRow.version.includes(deployment.versionNumber), 'Version #'); 62 assert.ok( 63 deploymentRow.submitTime.includes(moment(version.submitTime / 1000000).fromNow()), 64 'Submit time ago' 65 ); 66 }); 67 }); 68 69 test('when the deployment is running and needs promotion, the deployment item says so', function(assert) { 70 // Ensure the deployment needs deployment 71 const deployment = sortedDeployments.models[0]; 72 const taskGroupSummary = deployment.deploymentTaskGroupSummaryIds.map(id => 73 server.schema.deploymentTaskGroupSummaries.find(id) 74 )[0]; 75 76 deployment.update('status', 'running'); 77 deployment.save(); 78 79 taskGroupSummary.update({ 80 desiredCanaries: 1, 81 placedCanaries: [], 82 promoted: false, 83 }); 84 85 taskGroupSummary.save(); 86 87 Deployments.visit({ id: job.id }); 88 89 andThen(() => { 90 const deploymentRow = Deployments.deployments.objectAt(0); 91 assert.ok(deploymentRow.promotionIsRequired, 'Requires Promotion badge found'); 92 }); 93 }); 94 95 test('each deployment item can be opened to show details', function(assert) { 96 Deployments.visit({ id: job.id }); 97 98 andThen(() => { 99 const deploymentRow = Deployments.deployments.objectAt(0); 100 101 assert.notOk(deploymentRow.hasDetails, 'No deployment body'); 102 103 deploymentRow.toggle(); 104 105 andThen(() => { 106 assert.ok(deploymentRow.hasDetails, 'Deployment body found'); 107 }); 108 }); 109 }); 110 111 test('when open, a deployment shows the deployment metrics', function(assert) { 112 Deployments.visit({ id: job.id }); 113 114 andThen(() => { 115 const deployment = sortedDeployments.models[0]; 116 const deploymentRow = Deployments.deployments.objectAt(0); 117 const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id => 118 server.db.deploymentTaskGroupSummaries.find(id) 119 ); 120 121 deploymentRow.toggle(); 122 123 andThen(() => { 124 assert.equal( 125 deploymentRow.metricFor('canaries').text, 126 `${sum(taskGroupSummaries, 'placedCanaries', a => a.length)} / ${sum( 127 taskGroupSummaries, 128 'desiredCanaries' 129 )}`, 130 'Canaries, both places and desired, are in the metrics' 131 ); 132 133 assert.equal( 134 deploymentRow.metricFor('placed').text, 135 sum(taskGroupSummaries, 'placedAllocs'), 136 'Placed allocs aggregates across task groups' 137 ); 138 139 assert.equal( 140 deploymentRow.metricFor('desired').text, 141 sum(taskGroupSummaries, 'desiredTotal'), 142 'Desired allocs aggregates across task groups' 143 ); 144 145 assert.equal( 146 deploymentRow.metricFor('healthy').text, 147 sum(taskGroupSummaries, 'healthyAllocs'), 148 'Healthy allocs aggregates across task groups' 149 ); 150 151 assert.equal( 152 deploymentRow.metricFor('unhealthy').text, 153 sum(taskGroupSummaries, 'unhealthyAllocs'), 154 'Unhealthy allocs aggregates across task groups' 155 ); 156 157 assert.equal( 158 deploymentRow.notification, 159 deployment.statusDescription, 160 'Status description is in the metrics block' 161 ); 162 }); 163 }); 164 }); 165 166 test('when open, a deployment shows a list of all task groups and their respective stats', function(assert) { 167 Deployments.visit({ id: job.id }); 168 169 andThen(() => { 170 const deployment = sortedDeployments.models[0]; 171 const deploymentRow = Deployments.deployments.objectAt(0); 172 const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id => 173 server.db.deploymentTaskGroupSummaries.find(id) 174 ); 175 176 deploymentRow.toggle(); 177 178 andThen(() => { 179 assert.ok(deploymentRow.hasTaskGroups, 'Task groups found'); 180 181 assert.equal( 182 deploymentRow.taskGroups.length, 183 taskGroupSummaries.length, 184 'One row per task group' 185 ); 186 187 const taskGroup = taskGroupSummaries[0]; 188 const taskGroupRow = deploymentRow.taskGroups.objectAt(0); 189 190 assert.equal(taskGroupRow.name, taskGroup.name, 'Name'); 191 assert.equal(taskGroupRow.promotion, promotionTestForTaskGroup(taskGroup), 'Needs Promotion'); 192 assert.equal(taskGroupRow.autoRevert, taskGroup.autoRevert ? 'Yes' : 'No', 'Auto Revert'); 193 assert.equal( 194 taskGroupRow.canaries, 195 `${taskGroup.placedCanaries.length} / ${taskGroup.desiredCanaries}`, 196 'Canaries' 197 ); 198 assert.equal( 199 taskGroupRow.allocs, 200 `${taskGroup.placedAllocs} / ${taskGroup.desiredTotal}`, 201 'Allocs' 202 ); 203 assert.equal(taskGroupRow.healthy, taskGroup.healthyAllocs, 'Healthy Allocs'); 204 assert.equal(taskGroupRow.unhealthy, taskGroup.unhealthyAllocs, 'Unhealthy Allocs'); 205 assert.equal( 206 taskGroupRow.progress, 207 moment(taskGroup.requireProgressBy).format('MM/DD/YY HH:mm:ss'), 208 'Progress By' 209 ); 210 }); 211 }); 212 }); 213 214 test('when open, a deployment shows a list of all allocations for the deployment', function(assert) { 215 Deployments.visit({ id: job.id }); 216 217 andThen(() => { 218 const deployment = sortedDeployments.models[0]; 219 const deploymentRow = Deployments.deployments.objectAt(0); 220 221 // TODO: Make this less brittle. This logic is copied from the mirage config, 222 // since there is no reference to allocations on the deployment model. 223 const allocations = server.db.allocations.where({ jobId: deployment.jobId }).slice(0, 3); 224 225 deploymentRow.toggle(); 226 227 andThen(() => { 228 assert.ok(deploymentRow.hasAllocations, 'Allocations found'); 229 230 assert.equal(deploymentRow.allocations.length, allocations.length, 'One row per allocation'); 231 232 const allocation = allocations[0]; 233 const allocationRow = deploymentRow.allocations.objectAt(0); 234 235 assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'Allocation is as expected'); 236 }); 237 }); 238 }); 239 240 function classForStatus(status) { 241 const classMap = { 242 running: 'is-running', 243 successful: 'is-primary', 244 paused: 'is-light', 245 failed: 'is-error', 246 cancelled: 'is-cancelled', 247 }; 248 249 return classMap[status] || 'is-dark'; 250 } 251 252 function promotionTestForTaskGroup(taskGroup) { 253 if (taskGroup.desiredCanaries > 0 && taskGroup.promoted === false) { 254 return 'Yes'; 255 } else if (taskGroup.desiredCanaries > 0) { 256 return 'No'; 257 } 258 return 'N/A'; 259 }