github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/controllers/clients/client/index.js (about)

     1  /* eslint-disable ember/no-observers */
     2  /* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */
     3  import { alias } from '@ember/object/computed';
     4  import Controller from '@ember/controller';
     5  import { action, computed } from '@ember/object';
     6  import { observes } from '@ember-decorators/object';
     7  import { scheduleOnce } from '@ember/runloop';
     8  import { task } from 'ember-concurrency';
     9  import intersection from 'lodash.intersection';
    10  import Sortable from 'nomad-ui/mixins/sortable';
    11  import Searchable from 'nomad-ui/mixins/searchable';
    12  import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
    13  import {
    14    serialize,
    15    deserializedQueryParam as selection,
    16  } from 'nomad-ui/utils/qp-serialize';
    17  import classic from 'ember-classic-decorator';
    18  import localStorageProperty from 'nomad-ui/utils/properties/local-storage';
    19  
    20  @classic
    21  export default class ClientController extends Controller.extend(
    22    Sortable,
    23    Searchable
    24  ) {
    25    queryParams = [
    26      {
    27        currentPage: 'page',
    28      },
    29      {
    30        searchTerm: 'search',
    31      },
    32      {
    33        sortProperty: 'sort',
    34      },
    35      {
    36        sortDescending: 'desc',
    37      },
    38      {
    39        onlyPreemptions: 'preemptions',
    40      },
    41      {
    42        qpNamespace: 'namespace',
    43      },
    44      {
    45        qpJob: 'job',
    46      },
    47      {
    48        qpStatus: 'status',
    49      },
    50      'activeTask',
    51    ];
    52  
    53    // Set in the route
    54    flagAsDraining = false;
    55  
    56    qpNamespace = '';
    57    qpJob = '';
    58    qpStatus = '';
    59    currentPage = 1;
    60    pageSize = 8;
    61    activeTask = null;
    62  
    63    sortProperty = 'modifyIndex';
    64    sortDescending = true;
    65  
    66    @localStorageProperty('nomadShowSubTasks', false) showSubTasks;
    67  
    68    @action
    69    toggleShowSubTasks(e) {
    70      e.preventDefault();
    71      this.set('showSubTasks', !this.get('showSubTasks'));
    72    }
    73  
    74    @computed()
    75    get searchProps() {
    76      return ['shortId', 'name'];
    77    }
    78  
    79    onlyPreemptions = false;
    80  
    81    @computed('model.allocations.[]', 'preemptions.[]', 'onlyPreemptions')
    82    get visibleAllocations() {
    83      return this.onlyPreemptions ? this.preemptions : this.model.allocations;
    84    }
    85  
    86    @computed(
    87      'visibleAllocations.[]',
    88      'selectionNamespace',
    89      'selectionJob',
    90      'selectionStatus'
    91    )
    92    get filteredAllocations() {
    93      const { selectionNamespace, selectionJob, selectionStatus } = this;
    94  
    95      return this.visibleAllocations.filter((alloc) => {
    96        if (
    97          selectionNamespace.length &&
    98          !selectionNamespace.includes(alloc.get('namespace'))
    99        ) {
   100          return false;
   101        }
   102        if (
   103          selectionJob.length &&
   104          !selectionJob.includes(alloc.get('plainJobId'))
   105        ) {
   106          return false;
   107        }
   108        if (
   109          selectionStatus.length &&
   110          !selectionStatus.includes(alloc.clientStatus)
   111        ) {
   112          return false;
   113        }
   114        return true;
   115      });
   116    }
   117  
   118    @alias('filteredAllocations') listToSort;
   119    @alias('listSorted') listToSearch;
   120    @alias('listSearched') sortedAllocations;
   121  
   122    @selection('qpNamespace') selectionNamespace;
   123    @selection('qpJob') selectionJob;
   124    @selection('qpStatus') selectionStatus;
   125  
   126    eligibilityError = null;
   127    stopDrainError = null;
   128    drainError = null;
   129    showDrainNotification = false;
   130    showDrainUpdateNotification = false;
   131    showDrainStoppedNotification = false;
   132  
   133    @computed('model.allocations.@each.wasPreempted')
   134    get preemptions() {
   135      return this.model.allocations.filterBy('wasPreempted');
   136    }
   137  
   138    @computed('model.events.@each.time')
   139    get sortedEvents() {
   140      return this.get('model.events').sortBy('time').reverse();
   141    }
   142  
   143    @computed('model.drivers.@each.name')
   144    get sortedDrivers() {
   145      return this.get('model.drivers').sortBy('name');
   146    }
   147  
   148    @computed('model.hostVolumes.@each.name')
   149    get sortedHostVolumes() {
   150      return this.model.hostVolumes.sortBy('name');
   151    }
   152  
   153    @(task(function* (value) {
   154      try {
   155        yield value ? this.model.setEligible() : this.model.setIneligible();
   156      } catch (err) {
   157        const error = messageFromAdapterError(err) || 'Could not set eligibility';
   158        this.set('eligibilityError', error);
   159      }
   160    }).drop())
   161    setEligibility;
   162  
   163    @(task(function* () {
   164      try {
   165        this.set('flagAsDraining', false);
   166        yield this.model.cancelDrain();
   167        this.set('showDrainStoppedNotification', true);
   168      } catch (err) {
   169        this.set('flagAsDraining', true);
   170        const error = messageFromAdapterError(err) || 'Could not stop drain';
   171        this.set('stopDrainError', error);
   172      }
   173    }).drop())
   174    stopDrain;
   175  
   176    @(task(function* () {
   177      try {
   178        yield this.model.forceDrain({
   179          IgnoreSystemJobs: this.model.drainStrategy.ignoreSystemJobs,
   180        });
   181      } catch (err) {
   182        const error = messageFromAdapterError(err) || 'Could not force drain';
   183        this.set('drainError', error);
   184      }
   185    }).drop())
   186    forceDrain;
   187  
   188    @observes('model.isDraining')
   189    triggerDrainNotification() {
   190      if (!this.model.isDraining && this.flagAsDraining) {
   191        this.set('showDrainNotification', true);
   192      }
   193  
   194      this.set('flagAsDraining', this.model.isDraining);
   195    }
   196  
   197    @action
   198    gotoAllocation(allocation) {
   199      this.transitionToRoute('allocations.allocation', allocation);
   200    }
   201  
   202    @action
   203    setPreemptionFilter(value) {
   204      this.set('onlyPreemptions', value);
   205    }
   206  
   207    @action
   208    drainNotify(isUpdating) {
   209      this.set('showDrainUpdateNotification', isUpdating);
   210    }
   211  
   212    @action
   213    setDrainError(err) {
   214      const error = messageFromAdapterError(err) || 'Could not run drain';
   215      this.set('drainError', error);
   216    }
   217  
   218    get optionsAllocationStatus() {
   219      return [
   220        { key: 'pending', label: 'Pending' },
   221        { key: 'running', label: 'Running' },
   222        { key: 'complete', label: 'Complete' },
   223        { key: 'failed', label: 'Failed' },
   224        { key: 'lost', label: 'Lost' },
   225        { key: 'unknown', label: 'Unknown' },
   226      ];
   227    }
   228  
   229    @computed('model.allocations.[]', 'selectionJob', 'selectionNamespace')
   230    get optionsJob() {
   231      // Only show options for jobs in the selected namespaces, if any.
   232      const ns = this.selectionNamespace;
   233      const jobs = Array.from(
   234        new Set(
   235          this.model.allocations
   236            .filter((a) => ns.length === 0 || ns.includes(a.namespace))
   237            .mapBy('plainJobId')
   238        )
   239      ).compact();
   240  
   241      // Update query param when the list of jobs changes.
   242      scheduleOnce('actions', () => {
   243        // eslint-disable-next-line ember/no-side-effects
   244        this.set('qpJob', serialize(intersection(jobs, this.selectionJob)));
   245      });
   246  
   247      return jobs.sort().map((job) => ({ key: job, label: job }));
   248    }
   249  
   250    @computed('model.allocations.[]', 'selectionNamespace')
   251    get optionsNamespace() {
   252      const ns = Array.from(
   253        new Set(this.model.allocations.mapBy('namespace'))
   254      ).compact();
   255  
   256      // Update query param when the list of namespaces changes.
   257      scheduleOnce('actions', () => {
   258        // eslint-disable-next-line ember/no-side-effects
   259        this.set(
   260          'qpNamespace',
   261          serialize(intersection(ns, this.selectionNamespace))
   262        );
   263      });
   264  
   265      return ns.sort().map((n) => ({ key: n, label: n }));
   266    }
   267  
   268    setFacetQueryParam(queryParam, selection) {
   269      this.set(queryParam, serialize(selection));
   270    }
   271  
   272    @action
   273    setActiveTaskQueryParam(task) {
   274      if (task) {
   275        this.set('activeTask', `${task.allocation.id}-${task.name}`);
   276      } else {
   277        this.set('activeTask', null);
   278      }
   279    }
   280  }