conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

workers.tsx (5733B)


      1 // web/src/screen/workers.tsx - registered workers, and one worker's machines
      2 
      3 import m from 'mithril';
      4 import { get, page, patch, post, del, ApiError } from '../util/api';
      5 import { Badge, Empty, Failed, Gone, Loading, Pager, When } from '../component/layout';
      6 import { duration } from '../util/format';
      7 
      8 const LIMIT = 25;
      9 
     10 export const Workers: m.Component = {
     11   oninit(vnode: any) {
     12     vnode.state.offset = 0;
     13     vnode.state.rows = null;
     14     vnode.state.error = null;
     15     vnode.state.token = null;
     16     vnode.state.name = '';
     17 
     18     vnode.state.load = async () => {
     19       try {
     20         vnode.state.rows = await page<any>('/workers', {
     21           query: { offset: vnode.state.offset, limit: LIMIT },
     22         });
     23         vnode.state.error = null;
     24       } catch (e) {
     25         vnode.state.error = e;
     26       }
     27       m.redraw();
     28     };
     29     vnode.state.load();
     30   },
     31 
     32   view(vnode: any) {
     33     const { rows, error } = vnode.state;
     34     if (error && !rows) return <Failed error={error} />;
     35     if (!rows) return <Loading />;
     36 
     37     const create = async (e: Event) => {
     38       e.preventDefault();
     39       try {
     40         vnode.state.token = await post<any>('/workers', { name: vnode.state.name });
     41         vnode.state.name = '';
     42         await vnode.state.load();
     43       } catch (err) {
     44         vnode.state.error = err;
     45       }
     46     };
     47 
     48     return [
     49       <h1>workers</h1>,
     50       error ? <Failed error={error} /> : null,
     51 
     52       vnode.state.token
     53         ? (
     54           <div class="reveal">
     55             <strong>{`Token for ${vnode.state.token.name}`}</strong>
     56             <p class="muted">Copy it now. It cannot be shown again.</p>
     57             <code class="mono">{vnode.state.token.token}</code>
     58             <button onclick={() => { vnode.state.token = null; }}>done</button>
     59           </div>
     60         )
     61         : null,
     62 
     63       <form class="panel" onsubmit={create}>
     64         <label>
     65           <span>name</span>
     66           <input required value={vnode.state.name} oninput={(e: any) => { vnode.state.name = e.target.value; }} />
     67         </label>
     68         <button type="submit">register a worker</button>
     69       </form>,
     70 
     71       rows.rows.length === 0 ? <Empty what="workers" /> : (
     72         <table>
     73           <thead><tr><th>worker</th><th>enabled</th><th>last seen</th><th>address</th></tr></thead>
     74           <tbody>
     75             {rows.rows.map((w: any) => (
     76               <tr key={w.id}>
     77                 <td><m.route.Link href={`/workers/${w.id}`}>{w.name}</m.route.Link></td>
     78                 <td>{w.enabled ? 'yes' : 'no'}</td>
     79                 <td><When at={w.last_seen_at} /></td>
     80                 <td class="mono">{w.last_ip ?? ''}</td>
     81               </tr>
     82             ))}
     83           </tbody>
     84         </table>
     85       ),
     86 
     87       <Pager
     88         offset={rows.offset}
     89         total={rows.total}
     90         limit={LIMIT}
     91         onmove={(offset: number) => { vnode.state.offset = offset; vnode.state.load(); }}
     92       />,
     93     ];
     94   },
     95 };
     96 
     97 export const Worker: m.Component = {
     98   oninit(vnode: any) {
     99     const id = m.route.param('worker');
    100     vnode.state.id = id;
    101     vnode.state.worker = null;
    102     vnode.state.agents = [];
    103     vnode.state.tasks = [];
    104     vnode.state.error = null;
    105     vnode.state.gone = false;
    106 
    107     vnode.state.load = async () => {
    108       try {
    109         vnode.state.worker = await get<any>(`/workers/${id}`);
    110         vnode.state.agents = await get<any[]>(`/workers/${id}/agents`);
    111         vnode.state.tasks = (await page<any>(`/workers/${id}/tasks`, { query: { limit: 25 } })).rows;
    112         vnode.state.error = null;
    113       } catch (e) {
    114         if ((e as ApiError).status === 404) vnode.state.gone = true;
    115         else vnode.state.error = e;
    116       }
    117       m.redraw();
    118     };
    119     vnode.state.load();
    120   },
    121 
    122   view(vnode: any) {
    123     const { worker, error, gone, id } = vnode.state;
    124     if (gone) return <Gone what="worker" />;
    125     if (error && !worker) return <Failed error={error} />;
    126     if (!worker) return <Loading />;
    127 
    128     return [
    129       <h1>{worker.name}</h1>,
    130       error ? <Failed error={error} /> : null,
    131 
    132       <div class="actions">
    133         <label class="check">
    134           <input
    135             type="checkbox"
    136             checked={worker.enabled === 1}
    137             onchange={async (e: any) => {
    138               vnode.state.worker = await patch<any>(`/workers/${id}`, { enabled: e.target.checked });
    139             }}
    140           />
    141           <span>enabled</span>
    142         </label>
    143         <button
    144           onclick={async () => {
    145             await del(`/workers/${id}`);
    146             m.route.set('/workers');
    147           }}
    148         >
    149           remove
    150         </button>
    151       </div>,
    152 
    153       <h2>machines</h2>,
    154       vnode.state.agents.length === 0 ? <Empty what="machines" /> : (
    155         <table>
    156           <thead><tr><th>machine</th><th>architectures</th><th>features</th><th>last seen</th></tr></thead>
    157           <tbody>
    158             {vnode.state.agents.map((a: any) => (
    159               <tr key={a.id}>
    160                 <td>{a.name}</td>
    161                 <td class="mono">{(a.arches ?? []).join(', ')}</td>
    162                 <td class="mono">{(a.features ?? []).join(', ')}</td>
    163                 <td><When at={a.last_seen_at} /></td>
    164               </tr>
    165             ))}
    166           </tbody>
    167         </table>
    168       ),
    169 
    170       <h2>recent tasks</h2>,
    171       vnode.state.tasks.length === 0 ? <Empty what="tasks" /> : (
    172         <table>
    173           <tbody>
    174             {vnode.state.tasks.map((t: any) => (
    175               <tr key={t.id}>
    176                 <td><m.route.Link href={`/tasks/${t.id}`}>{t.name}</m.route.Link></td>
    177                 <td><Badge state={t.state} /></td>
    178                 <td>{duration(t.started_at, t.finished_at)}</td>
    179                 <td><When at={t.created_at} /></td>
    180               </tr>
    181             ))}
    182           </tbody>
    183         </table>
    184       ),
    185     ];
    186   },
    187 };