github.com/hernad/nomad@v1.6.112/ui/mirage/scenarios/default.js (about)

     1  /**
     2   * Copyright (c) HashiCorp, Inc.
     3   * SPDX-License-Identifier: MPL-2.0
     4   */
     5  
     6  import { assign } from '@ember/polyfills';
     7  import config from 'nomad-ui/config/environment';
     8  import * as topoScenarios from './topo';
     9  import * as sysbatchScenarios from './sysbatch';
    10  import { pickOne } from '../utils';
    11  import faker from 'nomad-ui/mirage/faker';
    12  
    13  const withNamespaces = getConfigValue('mirageWithNamespaces', false);
    14  const withTokens = getConfigValue('mirageWithTokens', true);
    15  const withRegions = getConfigValue('mirageWithRegions', false);
    16  
    17  export const allScenarios = {
    18    smallCluster,
    19    mediumCluster,
    20    largeCluster,
    21    massiveCluster,
    22    allJobTypes,
    23    allNodeTypes,
    24    everyFeature,
    25    emptyCluster,
    26    variableTestCluster,
    27    servicesTestCluster,
    28    policiesTestCluster,
    29    ...topoScenarios,
    30    ...sysbatchScenarios,
    31  };
    32  
    33  export const scenario =
    34    getScenarioQueryParameter() ||
    35    getConfigValue('mirageScenario', 'emptyCluster');
    36  
    37  export default function (server) {
    38    const activeScenario = allScenarios[scenario];
    39    if (!activeScenario) {
    40      throw new Error(
    41        `Selected Mirage scenario does not exist.\n\n${scenario} not in list: \n\n\t${Object.keys(
    42          allScenarios
    43        ).join('\n\t')}`
    44      );
    45    }
    46  
    47    // Make sure built-in node pools exist.
    48    createBuiltInNodePools(server);
    49    if (withNamespaces) createNamespaces(server);
    50    if (withTokens) createTokens(server);
    51    if (withRegions) createRegions(server);
    52  
    53    activeScenario(server);
    54  }
    55  
    56  // Scenarios
    57  
    58  function smallCluster(server) {
    59    faker.seed(1);
    60    server.create('feature', { name: 'Dynamic Application Sizing' });
    61    server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
    62    server.createList('node-pool', 2);
    63    server.createList('node', 5);
    64    server.create(
    65      'node',
    66      {
    67        name: 'node-with-meta',
    68        meta: { foo: 'bar', baz: 'qux' },
    69      },
    70      'withMeta'
    71    );
    72    server.createList('job', 1, { createRecommendations: true });
    73    server.create('job', {
    74      withGroupServices: true,
    75      withTaskServices: true,
    76      name: 'Service-haver',
    77      id: 'service-haver',
    78      namespaceId: 'default',
    79    });
    80    server.create('job', {
    81      createAllocations: true,
    82      groupTaskCount: 150,
    83      shallow: true,
    84      allocStatusDistribution: {
    85        running: 0.5,
    86        failed: 0.05,
    87        unknown: 0.2,
    88        lost: 0.1,
    89        complete: 0.1,
    90        pending: 0.05,
    91      },
    92      name: 'mixed-alloc-job',
    93      id: 'mixed-alloc-job',
    94      namespaceId: 'default',
    95      type: 'service',
    96      activeDeployment: true,
    97    });
    98  
    99    //#region Active Deployment
   100  
   101    const activelyDeployingJobGroups = 2;
   102    const activelyDeployingTasksPerGroup = 100;
   103  
   104    const activelyDeployingJob = server.create('job', {
   105      createAllocations: true,
   106      groupTaskCount: activelyDeployingTasksPerGroup,
   107      shallow: true,
   108      resourceSpec: Array(activelyDeployingJobGroups).fill(['M: 257, C: 500']),
   109      noDeployments: true, // manually created below
   110      activeDeployment: true,
   111      allocStatusDistribution: {
   112        running: 0.6,
   113        failed: 0.05,
   114        unknown: 0.05,
   115        lost: 0,
   116        complete: 0,
   117        pending: 0.3,
   118      },
   119      name: 'actively-deploying-job',
   120      id: 'actively-deploying-job',
   121      namespaceId: 'default',
   122      type: 'service',
   123    });
   124  
   125    server.create('deployment', false, 'active', {
   126      jobId: activelyDeployingJob.id,
   127      groupDesiredTotal: activelyDeployingTasksPerGroup,
   128      versionNumber: 1,
   129      status: 'running',
   130    });
   131    server.createList('allocation', 25, {
   132      jobId: activelyDeployingJob.id,
   133      jobVersion: 0,
   134      clientStatus: 'running',
   135    });
   136  
   137    // Manipulate the above job to show a nice distribution of running, canary, etc. allocs
   138    let activelyDeployingJobAllocs = server.schema.allocations
   139      .all()
   140      .filter((a) => a.jobId === activelyDeployingJob.id);
   141    activelyDeployingJobAllocs.models
   142      .filter((a) => a.clientStatus === 'running')
   143      .slice(0, 10)
   144      .forEach((a) =>
   145        a.update({ deploymentStatus: { Healthy: false, Canary: true } })
   146      );
   147    activelyDeployingJobAllocs.models
   148      .filter((a) => a.clientStatus === 'running')
   149      .slice(10, 20)
   150      .forEach((a) =>
   151        a.update({ deploymentStatus: { Healthy: true, Canary: true } })
   152      );
   153    activelyDeployingJobAllocs.models
   154      .filter((a) => a.clientStatus === 'running')
   155      .slice(20, 65)
   156      .forEach((a) =>
   157        a.update({ deploymentStatus: { Healthy: true, Canary: false } })
   158      );
   159    activelyDeployingJobAllocs.models
   160      .filter((a) => a.clientStatus === 'pending')
   161      .slice(0, 10)
   162      .forEach((a) =>
   163        a.update({ deploymentStatus: { Healthy: true, Canary: true } })
   164      );
   165    activelyDeployingJobAllocs.models
   166      .filter((a) => a.clientStatus === 'failed')
   167      .slice(0, 5)
   168      .forEach((a) =>
   169        a.update({ deploymentStatus: { Healthy: true, Canary: false } })
   170      );
   171  
   172    //#endregion Active Deployment
   173  
   174    server.create('job', {
   175      name: 'hcl-definition-job',
   176      id: 'display-hcl',
   177      namespaceId: 'default',
   178    });
   179    server.createList('allocFile', 5);
   180    server.create('allocFile', 'dir', { depth: 2 });
   181    server.createList('csi-plugin', 2);
   182    server.createList('variable', 3);
   183  
   184    const variableLinkedJob = server.db.jobs[0];
   185    const variableLinkedGroup = server.db.taskGroups.findBy({
   186      jobId: variableLinkedJob.id,
   187    });
   188    const variableLinkedTask = server.db.tasks.findBy({
   189      taskGroupId: variableLinkedGroup.id,
   190    });
   191    [
   192      'a/b/c/foo0',
   193      'a/b/c/bar1',
   194      'a/b/c/d/e/foo2',
   195      'a/b/c/d/e/bar3',
   196      'a/b/c/d/e/f/foo4',
   197      'a/b/c/d/e/f/g/foo5',
   198      'a/b/c/x/y/z/foo6',
   199      'a/b/c/x/y/z/bar7',
   200      'a/b/c/x/y/z/baz8',
   201      'w/x/y/foo9',
   202      'w/x/y/z/foo10',
   203      'w/x/y/z/bar11',
   204      'just some arbitrary file',
   205      'another arbitrary file',
   206      'another arbitrary file again',
   207    ].forEach((path) => server.create('variable', { id: path }));
   208  
   209    server.create('variable', {
   210      id: `nomad/jobs/${variableLinkedJob.id}/${variableLinkedGroup.name}/${variableLinkedTask.name}`,
   211      namespace: variableLinkedJob.namespace,
   212    });
   213  
   214    server.create('variable', {
   215      id: `nomad/jobs/${variableLinkedJob.id}/${variableLinkedGroup.name}`,
   216      namespace: variableLinkedJob.namespace,
   217    });
   218  
   219    server.create('variable', {
   220      id: `nomad/jobs/${variableLinkedJob.id}`,
   221      namespace: variableLinkedJob.namespace,
   222    });
   223  
   224    const newJobName = 'new-job';
   225    const newJobTaskGroupName = 'redis';
   226    const jsonJob = (overrides) => {
   227      return JSON.stringify(
   228        assign(
   229          {},
   230          {
   231            Name: newJobName,
   232            Namespace: 'default',
   233            Datacenters: ['dc1'],
   234            Priority: 50,
   235            TaskGroups: [
   236              {
   237                Name: newJobTaskGroupName,
   238                Tasks: [
   239                  {
   240                    Name: 'redis',
   241                    Driver: 'docker',
   242                  },
   243                ],
   244              },
   245            ],
   246          },
   247          overrides
   248        ),
   249        null,
   250        2
   251      );
   252    };
   253  
   254    server.create('variable', {
   255      id: `nomad/job-templates/foo-bar`,
   256      namespace: 'namespace-2',
   257      Items: {
   258        description: 'a description',
   259        template: jsonJob(),
   260      },
   261    });
   262  
   263    server.create('variable', {
   264      id: `nomad/job-templates/baz-qud`,
   265      namespace: 'default',
   266      Items: {
   267        description: 'another different description',
   268        template: jsonJob(),
   269      },
   270    });
   271  
   272    server.create('variable', {
   273      id: 'Auto-conflicting Variable',
   274      namespace: 'default',
   275    });
   276  
   277    // #region evaluations
   278  
   279    // Branching: a single eval that relates to N-1 mutually-unrelated evals
   280    const NUM_BRANCHING_EVALUATIONS = 3;
   281    Array(NUM_BRANCHING_EVALUATIONS)
   282      .fill()
   283      .map((_, i) => {
   284        return {
   285          evaluation: server.create('evaluation', {
   286            id: `branching_${i}`,
   287            previousEval: i > 0 ? `branching_0` : '',
   288            jobID: pickOne(server.db.jobs).id,
   289          }),
   290  
   291          evaluationStub: server.create('evaluation-stub', {
   292            id: `branching_${i}`,
   293            previousEval: i > 0 ? `branching_0` : '',
   294            status: 'failed',
   295          }),
   296        };
   297      })
   298      .map((x, i, all) => {
   299        x.evaluation.update({
   300          relatedEvals:
   301            i === 0
   302              ? all.filter((_, j) => j !== 0).map((e) => e.evaluation)
   303              : all.filter((_, j) => j !== i).map((e) => e.evaluation),
   304        });
   305        return x;
   306      });
   307  
   308    // Linear: a long line of N related evaluations
   309    const NUM_LINEAR_EVALUATIONS = 20;
   310    Array(NUM_LINEAR_EVALUATIONS)
   311      .fill()
   312      .map((_, i) => {
   313        return {
   314          evaluation: server.create('evaluation', {
   315            id: `linear_${i}`,
   316            previousEval: i > 0 ? `linear_${i - 1}` : '',
   317            jobID: pickOne(server.db.jobs).id,
   318          }),
   319  
   320          evaluationStub: server.create('evaluation-stub', {
   321            id: `linear_${i}`,
   322            previousEval: i > 0 ? `linear_${i - 1}` : '',
   323            nextEval: `linear_${i + 1}`,
   324            status: 'failed',
   325          }),
   326        };
   327      })
   328      .map((x, i, all) => {
   329        x.evaluation.update({
   330          relatedEvals: all.filter((_, j) => i !== j).map((e) => e.evaluation),
   331        });
   332        return x;
   333      });
   334  
   335    // #endregion evaluations
   336  
   337    const csiAllocations = server.createList('allocation', 5);
   338    const volumes = server.schema.csiVolumes.all().models;
   339    csiAllocations.forEach((alloc) => {
   340      const volume = pickOne(volumes);
   341      volume.writeAllocs.add(alloc);
   342      volume.readAllocs.add(alloc);
   343      volume.save();
   344    });
   345  
   346    server.create('auth-method', { name: 'vault' });
   347    server.create('auth-method', { name: 'auth0' });
   348    server.create('auth-method', { name: 'cognito' });
   349    server.create('auth-method', { name: 'JWT-Local', type: 'JWT' });
   350  }
   351  
   352  function mediumCluster(server) {
   353    server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
   354    server.createList('node-pool', 5);
   355    server.createList('node', 50);
   356    server.createList('job', 25);
   357  }
   358  
   359  function variableTestCluster(server) {
   360    faker.seed(1);
   361    createTokens(server);
   362    createNamespaces(server);
   363    server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
   364    server.createList('node-pool', 3);
   365    server.createList('node', 5);
   366    server.createList('job', 3);
   367    server.createList('variable', 3);
   368    // server.createList('allocFile', 5);
   369    // server.create('allocFile', 'dir', { depth: 2 });
   370    // server.createList('csi-plugin', 2);
   371  
   372    const variableLinkedJob = server.db.jobs[0];
   373    const variableLinkedGroup = server.db.taskGroups.findBy({
   374      jobId: variableLinkedJob.id,
   375    });
   376    const variableLinkedTask = server.db.tasks.findBy({
   377      taskGroupId: variableLinkedGroup.id,
   378    });
   379    [
   380      'a/b/c/foo0',
   381      'a/b/c/bar1',
   382      'a/b/c/d/e/foo2',
   383      'a/b/c/d/e/bar3',
   384      'a/b/c/d/e/f/foo4',
   385      'a/b/c/d/e/f/g/foo5',
   386      'a/b/c/x/y/z/foo6',
   387      'a/b/c/x/y/z/bar7',
   388      'a/b/c/x/y/z/baz8',
   389      'w/x/y/foo9',
   390      'w/x/y/z/foo10',
   391      'w/x/y/z/bar11',
   392    ].forEach((path) => server.create('variable', { id: path }));
   393  
   394    server.create('variable', {
   395      id: `nomad/jobs/${variableLinkedJob.id}/${variableLinkedGroup.name}/${variableLinkedTask.name}`,
   396      namespace: variableLinkedJob.namespace,
   397    });
   398  
   399    server.create('variable', {
   400      id: `nomad/jobs/${variableLinkedJob.id}/${variableLinkedGroup.name}`,
   401      namespace: variableLinkedJob.namespace,
   402    });
   403  
   404    server.create('variable', {
   405      id: `nomad/jobs/${variableLinkedJob.id}`,
   406      namespace: variableLinkedJob.namespace,
   407    });
   408  
   409    server.create('variable', {
   410      id: 'just some arbitrary file',
   411      namespace: 'namespace-2',
   412    });
   413  
   414    server.create('variable', {
   415      id: 'another arbitrary file',
   416      namespace: 'namespace-2',
   417    });
   418  
   419    server.create('variable', {
   420      id: 'another arbitrary file again',
   421      namespace: 'namespace-2',
   422    });
   423  
   424    server.create('variable', {
   425      id: 'Auto-conflicting Variable',
   426      namespace: 'default',
   427    });
   428  }
   429  
   430  function policiesTestCluster(server) {
   431    faker.seed(1);
   432    createTokens(server);
   433    server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
   434  }
   435  
   436  function servicesTestCluster(server) {
   437    faker.seed(1);
   438    server.create('feature', { name: 'Dynamic Application Sizing' });
   439    server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
   440    server.createList('node-pool', 3);
   441    server.createList('node', 5);
   442    server.createList('job', 1, { createRecommendations: true });
   443    server.create('job', {
   444      withGroupServices: true,
   445      withTaskServices: true,
   446      name: 'Service-haver',
   447      id: 'service-haver',
   448      namespaceId: 'default',
   449    });
   450    server.createList('allocFile', 5);
   451    server.create('allocFile', 'dir', { depth: 2 });
   452    server.createList('csi-plugin', 2);
   453    server.createList('variable', 3);
   454  
   455    const variableLinkedJob = server.db.jobs[0];
   456    const variableLinkedGroup = server.db.taskGroups.findBy({
   457      jobId: variableLinkedJob.id,
   458    });
   459    const variableLinkedTask = server.db.tasks.findBy({
   460      taskGroupId: variableLinkedGroup.id,
   461    });
   462    [
   463      'a/b/c/foo0',
   464      'a/b/c/bar1',
   465      'a/b/c/d/e/foo2',
   466      'a/b/c/d/e/bar3',
   467      'a/b/c/d/e/f/foo4',
   468      'a/b/c/d/e/f/g/foo5',
   469      'a/b/c/x/y/z/foo6',
   470      'a/b/c/x/y/z/bar7',
   471      'a/b/c/x/y/z/baz8',
   472      'w/x/y/foo9',
   473      'w/x/y/z/foo10',
   474      'w/x/y/z/bar11',
   475      'just some arbitrary file',
   476      'another arbitrary file',
   477      'another arbitrary file again',
   478    ].forEach((path) => server.create('variable', { id: path }));
   479  
   480    server.create('variable', {
   481      id: `nomad/jobs/${variableLinkedJob.id}/${variableLinkedGroup.name}/${variableLinkedTask.name}`,
   482      namespace: variableLinkedJob.namespace,
   483    });
   484  
   485    server.create('variable', {
   486      id: `nomad/jobs/${variableLinkedJob.id}/${variableLinkedGroup.name}`,
   487      namespace: variableLinkedJob.namespace,
   488    });
   489  
   490    server.create('variable', {
   491      id: `nomad/jobs/${variableLinkedJob.id}`,
   492      namespace: variableLinkedJob.namespace,
   493    });
   494  
   495    server.create('variable', {
   496      id: 'Auto-conflicting Variable',
   497      namespace: 'default',
   498    });
   499  
   500    // #region evaluations
   501  
   502    // Branching: a single eval that relates to N-1 mutually-unrelated evals
   503    const NUM_BRANCHING_EVALUATIONS = 3;
   504    Array(NUM_BRANCHING_EVALUATIONS)
   505      .fill()
   506      .map((_, i) => {
   507        return {
   508          evaluation: server.create('evaluation', {
   509            id: `branching_${i}`,
   510            previousEval: i > 0 ? `branching_0` : '',
   511            jobID: pickOne(server.db.jobs).id,
   512          }),
   513  
   514          evaluationStub: server.create('evaluation-stub', {
   515            id: `branching_${i}`,
   516            previousEval: i > 0 ? `branching_0` : '',
   517            status: 'failed',
   518          }),
   519        };
   520      })
   521      .map((x, i, all) => {
   522        x.evaluation.update({
   523          relatedEvals:
   524            i === 0
   525              ? all.filter((_, j) => j !== 0).map((e) => e.evaluation)
   526              : all.filter((_, j) => j !== i).map((e) => e.evaluation),
   527        });
   528        return x;
   529      });
   530  
   531    // Linear: a long line of N related evaluations
   532    const NUM_LINEAR_EVALUATIONS = 20;
   533    Array(NUM_LINEAR_EVALUATIONS)
   534      .fill()
   535      .map((_, i) => {
   536        return {
   537          evaluation: server.create('evaluation', {
   538            id: `linear_${i}`,
   539            previousEval: i > 0 ? `linear_${i - 1}` : '',
   540            jobID: pickOne(server.db.jobs).id,
   541          }),
   542  
   543          evaluationStub: server.create('evaluation-stub', {
   544            id: `linear_${i}`,
   545            previousEval: i > 0 ? `linear_${i - 1}` : '',
   546            nextEval: `linear_${i + 1}`,
   547            status: 'failed',
   548          }),
   549        };
   550      })
   551      .map((x, i, all) => {
   552        x.evaluation.update({
   553          relatedEvals: all.filter((_, j) => i !== j).map((e) => e.evaluation),
   554        });
   555        return x;
   556      });
   557  
   558    // #endregion evaluations
   559  
   560    const csiAllocations = server.createList('allocation', 5);
   561    const volumes = server.schema.csiVolumes.all().models;
   562    csiAllocations.forEach((alloc) => {
   563      const volume = pickOne(volumes);
   564      volume.writeAllocs.add(alloc);
   565      volume.readAllocs.add(alloc);
   566      volume.save();
   567    });
   568  }
   569  
   570  // Due to Mirage performance, large cluster scenarios will be slow
   571  function largeCluster(server) {
   572    server.createList('agent', 5);
   573    server.createList('node-pool', 10);
   574    server.createList('node', 1000);
   575    server.createList('job', 100);
   576  }
   577  
   578  function massiveCluster(server) {
   579    server.createList('agent', 7);
   580    server.createList('node-pool', 100);
   581    server.createList('node', 5000);
   582    server.createList('job', 2000);
   583  }
   584  
   585  function allJobTypes(server) {
   586    server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
   587    server.createList('node', 5);
   588  
   589    server.create('job', { type: 'service' });
   590    server.create('job', { type: 'batch' });
   591    server.create('job', { type: 'system' });
   592    server.create('job', 'periodic');
   593    server.create('job', 'parameterized');
   594    server.create('job', 'periodicSysbatch');
   595    server.create('job', 'parameterizedSysbatch');
   596    server.create('job', { failedPlacements: true });
   597  }
   598  
   599  function allNodeTypes(server) {
   600    server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
   601  
   602    server.create('node');
   603    server.create('node', 'forceIPv4');
   604    server.create('node', 'draining');
   605    server.create('node', 'forcedDraining');
   606    server.create('node', 'noDeadlineDraining');
   607    server.create('node', 'withMeta');
   608  
   609    server.createList('job', 3);
   610  }
   611  
   612  function everyFeature(server) {
   613    server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
   614    server.createList('node-pool', 3);
   615  
   616    server.create('node', 'forceIPv4');
   617    server.create('node', 'draining');
   618    server.create('node', 'forcedDraining');
   619    server.create('node', 'noDeadlineDraining');
   620    server.create('node', 'withMeta');
   621  
   622    const job1 = server.create('job', {
   623      type: 'service',
   624      activeDeployment: true,
   625      namespaceId: 'default',
   626      createAllocations: false,
   627    });
   628    server.create('job', {
   629      type: 'batch',
   630      failedPlacements: true,
   631      namespaceId: 'default',
   632    });
   633    server.create('job', { type: 'system', namespaceId: 'default' });
   634    server.create('job', 'periodic', { namespaceId: 'default' });
   635    server.create('job', 'parameterized', { namespaceId: 'default' });
   636  
   637    server.create('allocation', 'rescheduled', { jobId: job1.id });
   638    server.create('allocation', 'preempter', { jobId: job1.id });
   639    server.create('allocation', 'preempted', { jobId: job1.id });
   640  }
   641  
   642  function emptyCluster(server) {
   643    server.create('agent');
   644    server.create('node');
   645  }
   646  
   647  // Behaviors
   648  
   649  function createBuiltInNodePools(server) {
   650    server.create('node-pool', { name: 'default' });
   651    server.create('node-pool', { name: 'all' });
   652  }
   653  
   654  function createTokens(server) {
   655    server.createList('token', 3);
   656    server.create('token', {
   657      name: 'Secure McVariables',
   658      id: '53cur3-v4r14bl35',
   659    });
   660    server.create('token', {
   661      name: "Safe O'Constants",
   662      id: 'f3w3r-53cur3-v4r14bl35',
   663    });
   664    server.create('token', {
   665      name: 'Lazarus MacMarbh',
   666      id: '3XP1R35-1N-3L3V3N-M1NU735',
   667    });
   668    logTokens(server);
   669  }
   670  
   671  function createNamespaces(server) {
   672    server.createList('namespace', 3);
   673  }
   674  
   675  function createRegions(server) {
   676    ['americas', 'europe', 'asia', 'some-long-name-just-to-test'].forEach(
   677      (id) => {
   678        server.create('region', { id });
   679      }
   680    );
   681  }
   682  
   683  /* eslint-disable */
   684  function logTokens(server) {
   685    console.log('TOKENS:');
   686    server.db.tokens.forEach((token) => {
   687      console.log(`
   688  Name: ${token.name}
   689  Secret: ${token.secretId}
   690  Accessor: ${token.accessorId}
   691  
   692  `);
   693    });
   694  
   695    console.log(
   696      'Alternatively, log in with a JWT. If it ends with `management`, you have full access. If it ends with `bad`, you`ll get an error. Otherwise, you`ll get a token with limited access.'
   697    );
   698    console.log('=====================================');
   699  }
   700  
   701  function getConfigValue(variableName, defaultValue) {
   702    const value = config.APP[variableName];
   703    if (value !== undefined) return value;
   704  
   705    console.warn(
   706      `No ENV.APP value set for "${variableName}". Defaulting to "${defaultValue}". To set a custom value, modify config/environment.js`
   707    );
   708    return defaultValue;
   709  }
   710  
   711  function getScenarioQueryParameter() {
   712    const params = new URLSearchParams(window.location.search);
   713    return params.get('mirage-scenario');
   714  }
   715  /* eslint-enable */