Skip to content

Additional Fix

A fix is used as addition to tests. They are added to the test instance on case of a problem (error or warning).

Then after the test was done the following steps are possible:

  1. Analyze

    The analyzation will take place directly after a test has finished. It should create a description explaining the values from the test and interpreting it's meaning and what to do to optimize this.

  2. Repair

    If the findings from the analyzation can be solved by automatic repair this method will do it.

Environment

  • this.test - test instance to which the fix belongs to
  • this.data - fix data from configuration
  • this.lang - language to use in descriptions
  • this.context - context data to be used
  • this.debug - debug logger

Methods to Implement

  • async this.analyze ()
  • async this.repair ()

Result Elements

  • this.type:
    • manual - only show description, user has to fix manual
    • auto - automatic repair is possible
  • this.title - title of fix
  • this.description - long descriptive text
  • this.action - action taken in repair
  • this.status:
    • MANUAL - manual test
    • NODATA - could not analyze
    • IRRELEVANT - nothing to be done
    • OK - repair successful
    • ERROR - repair failed
  • this.error - error message on repair problem
  • this._log - Output log list. Each line should start with the verbosity level followed by colon.

Log Helper

Each of the following log helper will do the same, add the element to the this._log element with the correct verbose level:

  • this.logFix (msg: string, log: boolean = true)
  • this.logProcess (msg: any, log: boolean = true)
  • this.logStdout (msg: string, log: boolean = true)
  • this.logStderr (msg: string, log: boolean = true)
  • this.logCode (msg: number, log: boolean = true)

Execution Helper

First the shell execution is integrated with handy functions:

  • this.shell (fn: () => Promise<any>)
  • this.execute (cmd: string, log: boolean = true): Promise<any>
  • this.commands (list: string[], sudo = false)

And then other (sub) tests can be made as further analyzation. So if in example a service won't work check the tcp port and ping if there is a network problem;

  • this.subTest (setup: any): Promise<any>
  • this.subTests (setup: any[], analyze = false): Promise<any[]>

Basic Structure

import Fix from '.'
import { T } from '../i18n';

class FixXxx extends Fix {

  public async analyze () {
    const t = T(this.lang, 'linux')
    this.title = t('os.fix.title')

    // WRITE description
    // SET status
  }

  // only if repair is implemented
  public async repair () {
    const t = T(this.lang, 'linux')
    this.action = t('os.fix.repair') + '\n'

    // REPAIR
    // SET status, error
  }
}

export default FixLinuxOS

Full Example

import * as Builder from '@alinex/validator/lib/schema';
import { inspect } from 'util'

import FixLinux from '.'
import { T } from '../../i18n';
//import { indent } from '../../string';

class FixLinuxOS extends FixLinux {

  public async analyze () {
    const t = T(this.lang, 'linux')
    this.title = t('os.fix.title')
    this.debug('analyze')

    // mongodb test
    const result = this.test.result
    let msg = ''
    if (result.security) msg += t('os.fix.security', { count: result.security }) + '\n'
    if (result.updates) msg += t('os.fix.updates', { count: result.updates }) + '\n'
    if (result.removes) msg += t('os.fix.removes', { count: result.removes }) + '\n'
    if (msg) {
      this.type = 'auto'
      if (result.distBase === 'debian') msg += t('os.fix.upgradeDebian')
      if (result.distBase === 'arch') msg += t('os.fix.upgradeArch')
    }
    if (msg) {
      this.description += msg
      this.status = 'MANUAL'
    } else this.status = 'IRRELEVANT'
  }

  public async repair () {
    const t = T(this.lang, 'linux')
    this.action = t('os.fix.repair') + '\n'
    this.debug('repair')

    const result = this.test.result
    await this.shell(async () => {
      // commands
      let cmds = await this.commands(['apt', 'shutdown'], true)
      const nosudo = Object.keys(cmds).filter(e => !cmds[e]).map(e => inspect(e.substr(1)))
      if (nosudo.join()) {
        this.status = 'NODATA'
        this.error = new Error(t('core:fix.nosudoRepair', { count: nosudo.length, cmd: nosudo.join(', ') }))
        return
      }
      if (result.updates || result.removes) {
        await this.execute(`${cmds.$apt} upgrade -y`)
        .then(res => {
          if (res.exitCode) throw new Error(`Failed upgrade with status code ${res.exitCode}.`)
          else this.status = 'OK'
        })
        await this.execute(`${cmds.$apt} autoremove --purge -y`)
          .then(res => {
            if (res.exitCode) throw new Error(`Failed autoremove with status code ${res.exitCode}.`)
            else this.status = 'OK'
          })
      }
      // reboot
      await this.execute(`test -e /var/run/reboot-required && ${cmds.$shutdown} -r now`)
        .then(res => {
          if (res.exitCode) throw new Error(`Failed to reboot system code: ${res.exitCode}.`)
        else this.status = 'OK'
        })
    })
      // general error
      .catch((e: Error) => {
        this.status = 'ERROR'
        this.error = e
      })
  }
}

export default FixLinuxOS