github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/ui/mirage/config.js (about) 1 import Ember from 'ember'; 2 import Response from 'ember-cli-mirage/response'; 3 import { HOSTS } from './common'; 4 import { logFrames, logEncode } from './data/logs'; 5 6 const { copy } = Ember; 7 8 export function findLeader(schema) { 9 const agent = schema.agents.first(); 10 return `${agent.address}:${agent.tags.port}`; 11 } 12 13 export default function() { 14 this.timing = 0; // delay for each request, automatically set to 0 during testing 15 16 this.namespace = 'v1'; 17 this.trackRequests = Ember.testing; 18 19 const nomadIndices = {}; // used for tracking blocking queries 20 const server = this; 21 const withBlockingSupport = function(fn) { 22 return function(schema, request) { 23 // Get the original response 24 let { url } = request; 25 url = url.replace(/index=\d+[&;]?/, ''); 26 const response = fn.apply(this, arguments); 27 28 // Get and increment the approrpriate index 29 nomadIndices[url] || (nomadIndices[url] = 1); 30 const index = nomadIndices[url]; 31 nomadIndices[url]++; 32 33 // Annotate the response with the index 34 if (response instanceof Response) { 35 response.headers['X-Nomad-Index'] = index; 36 return response; 37 } 38 return new Response(200, { 'x-nomad-index': index }, response); 39 }; 40 }; 41 42 this.get( 43 '/jobs', 44 withBlockingSupport(function({ jobs }, { queryParams }) { 45 const json = this.serialize(jobs.all()); 46 const namespace = queryParams.namespace || 'default'; 47 return json 48 .filter( 49 job => 50 namespace === 'default' 51 ? !job.NamespaceID || job.NamespaceID === namespace 52 : job.NamespaceID === namespace 53 ) 54 .map(job => filterKeys(job, 'TaskGroups', 'NamespaceID')); 55 }) 56 ); 57 58 this.get( 59 '/job/:id', 60 withBlockingSupport(function({ jobs }, { params, queryParams }) { 61 const job = jobs.all().models.find(job => { 62 const jobIsDefault = !job.namespaceId || job.namespaceId === 'default'; 63 const qpIsDefault = !queryParams.namespace || queryParams.namespace === 'default'; 64 return ( 65 job.id === params.id && 66 (job.namespaceId === queryParams.namespace || (jobIsDefault && qpIsDefault)) 67 ); 68 }); 69 70 return job ? this.serialize(job) : new Response(404, {}, null); 71 }) 72 ); 73 74 this.get( 75 '/job/:id/summary', 76 withBlockingSupport(function({ jobSummaries }, { params }) { 77 return this.serialize(jobSummaries.findBy({ jobId: params.id })); 78 }) 79 ); 80 81 this.get('/job/:id/allocations', function({ allocations }, { params }) { 82 return this.serialize(allocations.where({ jobId: params.id })); 83 }); 84 85 this.get('/job/:id/versions', function({ jobVersions }, { params }) { 86 return this.serialize(jobVersions.where({ jobId: params.id })); 87 }); 88 89 this.get('/job/:id/deployments', function({ deployments }, { params }) { 90 return this.serialize(deployments.where({ jobId: params.id })); 91 }); 92 93 this.post('/job/:id/periodic/force', function(schema, { params }) { 94 // Create the child job 95 const parent = schema.jobs.find(params.id); 96 97 // Use the server instead of the schema to leverage the job factory 98 server.create('job', 'periodicChild', { 99 parentId: parent.id, 100 namespaceId: parent.namespaceId, 101 namespace: parent.namespace, 102 createAllocations: parent.createAllocations, 103 }); 104 105 // Return bogus, since the response is normally just eval information 106 return new Response(200, {}, '{}'); 107 }); 108 109 this.get('/deployment/:id'); 110 111 this.get('/job/:id/evaluations', function({ evaluations }, { params }) { 112 return this.serialize(evaluations.where({ jobId: params.id })); 113 }); 114 115 this.get('/evaluation/:id'); 116 117 this.get('/deployment/allocations/:id', function(schema, { params }) { 118 const job = schema.jobs.find(schema.deployments.find(params.id).jobId); 119 const allocations = schema.allocations.where({ jobId: job.id }); 120 121 return this.serialize(allocations.slice(0, 3)); 122 }); 123 124 this.get('/nodes', function({ nodes }) { 125 const json = this.serialize(nodes.all()); 126 return json; 127 }); 128 129 this.get('/node/:id'); 130 131 this.get('/node/:id/allocations', function({ allocations }, { params }) { 132 return this.serialize(allocations.where({ nodeId: params.id })); 133 }); 134 135 this.get('/allocation/:id'); 136 137 this.get('/namespaces', function({ namespaces }) { 138 const records = namespaces.all(); 139 140 if (records.length) { 141 return this.serialize(records); 142 } 143 144 return new Response(501, {}, null); 145 }); 146 147 this.get('/namespace/:id', function({ namespaces }, { params }) { 148 if (namespaces.all().length) { 149 return this.serialize(namespaces.find(params.id)); 150 } 151 152 return new Response(501, {}, null); 153 }); 154 155 this.get('/agent/members', function({ agents }) { 156 return { 157 Members: this.serialize(agents.all()), 158 }; 159 }); 160 161 this.get('/status/leader', function(schema) { 162 return JSON.stringify(findLeader(schema)); 163 }); 164 165 this.get('/acl/token/self', function({ tokens }, req) { 166 const secret = req.requestHeaders['X-Nomad-Token']; 167 const tokenForSecret = tokens.findBy({ secretId: secret }); 168 169 // Return the token if it exists 170 if (tokenForSecret) { 171 return this.serialize(tokenForSecret); 172 } 173 174 // Client error if it doesn't 175 return new Response(400, {}, null); 176 }); 177 178 this.get('/acl/token/:id', function({ tokens }, req) { 179 const token = tokens.find(req.params.id); 180 const secret = req.requestHeaders['X-Nomad-Token']; 181 const tokenForSecret = tokens.findBy({ secretId: secret }); 182 183 // Return the token only if the request header matches the token 184 // or the token is of type management 185 if (token.secretId === secret || (tokenForSecret && tokenForSecret.type === 'management')) { 186 return this.serialize(token); 187 } 188 189 // Return not authorized otherwise 190 return new Response(403, {}, null); 191 }); 192 193 this.get('/acl/policy/:id', function({ policies, tokens }, req) { 194 const policy = policies.find(req.params.id); 195 const secret = req.requestHeaders['X-Nomad-Token']; 196 const tokenForSecret = tokens.findBy({ secretId: secret }); 197 198 // Return the policy only if the token that matches the request header 199 // includes the policy or if the token that matches the request header 200 // is of type management 201 if ( 202 tokenForSecret && 203 (tokenForSecret.policies.includes(policy) || tokenForSecret.type === 'management') 204 ) { 205 return this.serialize(policy); 206 } 207 208 // Return not authorized otherwise 209 return new Response(403, {}, null); 210 }); 211 212 const clientAllocationStatsHandler = function({ clientAllocationStats }, { params }) { 213 return this.serialize(clientAllocationStats.find(params.id)); 214 }; 215 216 const clientAllocationLog = function(server, { params, queryParams }) { 217 const allocation = server.allocations.find(params.allocation_id); 218 const tasks = allocation.taskStateIds.map(id => server.taskStates.find(id)); 219 220 if (!tasks.mapBy('name').includes(queryParams.task)) { 221 return new Response(400, {}, 'must include task name'); 222 } 223 224 if (queryParams.plain) { 225 return logFrames.join(''); 226 } 227 228 return logEncode(logFrames, logFrames.length - 1); 229 }; 230 231 // Client requests are available on the server and the client 232 this.get('/client/allocation/:id/stats', clientAllocationStatsHandler); 233 this.get('/client/fs/logs/:allocation_id', clientAllocationLog); 234 235 this.get('/client/v1/client/stats', function({ clientStats }, { queryParams }) { 236 return this.serialize(clientStats.find(queryParams.node_id)); 237 }); 238 239 // TODO: in the future, this hack may be replaceable with dynamic host name 240 // support in pretender: https://github.com/pretenderjs/pretender/issues/210 241 HOSTS.forEach(host => { 242 this.get(`http://${host}/v1/client/allocation/:id/stats`, clientAllocationStatsHandler); 243 this.get(`http://${host}/v1/client/fs/logs/:allocation_id`, clientAllocationLog); 244 245 this.get(`http://${host}/v1/client/stats`, function({ clientStats }) { 246 return this.serialize(clientStats.find(host)); 247 }); 248 }); 249 } 250 251 function filterKeys(object, ...keys) { 252 const clone = copy(object, true); 253 254 keys.forEach(key => { 255 delete clone[key]; 256 }); 257 258 return clone; 259 }