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  }