-1

I have a webserver that takes user input for a Docker container, creates a global_vars file (Ansible variables file), it then copy paste's the appropriate group_vars file to the Ansible folder, finally it runs the ansible-playbook. What I want to achieve is getting the output from the spawn command which runs the Ansible Playbook and give a live stream of the output so that the user knows when their container is ready for use.

I'm using express-handlebars for templating and I've already tried -

//Render Ansible output
app.get('/control-setup/:userid/output', async (req,res) => {
  let userid = req.params.userid
  Users.findById(userid).exec(async function (err, result) {
    if (err) throw err
    let site = result.docker.site
    let site_name = site.slice(8)
    let output = await exphbs.create({
      helpers: {
        outputHelper: function () {return ansible.ansibleRunAndReturn(site_name)}
      }
    })
    res.render('output', {
      helpers: {
        output: output
      }
    })
    })
  })

However I don't get any output on the webpage, however I can see the output on my console.

Ansible spawn script -

//Run Ansible and report errors
async function ansibleRunAndReturn(site_name) {
//Copy relevant global file to mujoerp_automation/glabal_vars/all
const src = await __dirname+'/ansible/'+site_name
const dest = await path.join(__dirname, '../mujoerp_automation/group_vars/all')
await fse.copy(src, dest, function (err) {
  if (err) throw err
  console.log(src+ ' Global vars file copied to ' +dest)
})
//Change playbook location as required
const playbook = await path.join(__dirname, '../mujoerp_automation/master.yml')
const process = await spawn('sh', ['-c', 'echo redhat237 | sudo -S ansible-playbook ../mujoerp_automation/master.yml'],{stdio: 'inherit' })
return process
}

module.exports = {
  createGlobal,
  ansibleRunAndReturn
}

The output webpage -

<div>
  {{output.process}}
</div>

Could someone guide me with what I'm doing wrong or if there's a better approach?

Firdaus Aga
  • 27
  • 2
  • 6

1 Answers1

0

I wasn't able to get the output in a live stream, however I made the site work by using web-pages instead.

The Ansible function starts when I redirect to another page after clicking the setup button.

Few lessons - Shifted from spawn to spawnSync. Removed promisify. Added a conditional to throw an exception for the try-catch block

Changed ansible.js code -

//Run Ansible and report errors
async function ansibleRunAndReturn(site_name) {
//Copy relevant global file to mujoerp_automation/glabal_vars/all
const src = await __dirname+'/ansible/'+site_name
const dest = await path.join(__dirname, '../mujoerp_automation/group_vars/all')
await fse.copy(src, dest, function (err) {
  if (err) throw err
  console.log(src+ ' Global vars file copied to ' +dest)
})
//Change playbook location as required
const playbook = await path.join(__dirname, '../mujoerp_automation/master.yml')
const ansibleProcess = await spawnSync('sh', ['-c', `echo redhat237 | sudo -S ansible-playbook ${playbook}`], {encoding: 'utf8'})//stdio: 'inherit', })
const stdout = await console.log(ansibleProcess.stdout)
await console.log(ansibleProcess.stderr)
const output = await console.log(ansibleProcess.output)
await console.log(ansibleProcess.status)
if (ansibleProcess.status != 0) {
  throw 'Server setup failed'
}

return {playbook, ansibleProcess, stdout, output }
}

Changed Route for the "Control-Centre"

//Render Control Setup
app.get('/control-setup/:userid', (req,res) => {
  let userid = req.params.userid
  let email = Users.findById(userid).exec(function (err, data) {
    if (err) throw error
    res.render('controlSetup', {
      helpers: {
        userid: function () { return req.params.userid },
        email: data.email,
        site: data.docker.site,
        admin: data.docker.adminpwd,
        cpus: data.docker.cpus,
        memory: data.docker.memory,
        storage: data.docker.storage
      }
    })
  })
})

//Render Control Edit
app.get('/control-edit/:userid', (req,res) => {
  let userid = req.params.userid
  let email = Users.findById(userid).exec(function (err, data) {
    if (err) throw error
    res.render('controlEdit', {
      helpers: {
        userid: function () { return req.params.userid },
        email: data.email,
        cpus: data.docker.cpus,
        memory: data.docker.memory,
        storage: data.docker.storage
      }
    })
  })
})

//Render Ansible output
app.get('/control-setup/:userid/output', async (req,res) => {
  let userid = await req.params.userid
  await Users.findById(userid).exec(async function (err, result) {
    if (err) throw err
    let site = result.docker.site
    let site_name = site.slice(8)
    try {
      await res.render('output')
      await ansible.ansibleRunAndReturn(site_name)
      await res.redirect('/docker/control/'+userid)
    } catch (e) {
      res.redirect('/docker/control-setup/'+userid+'/error')
    }
  })
})

app.get('/control-setup/:userid/error', async (req,res) => {
  res.render('error')
})

//Setup/Edit Docker details for docker
app.post('/control-setup/:userid',
//validation
[
  body('site').isURL({allow_underscores: true}),
  body('admin').isLength({ min: 6 })
], async (req,res) => {
   const errors = validationResult(req);
     if (!errors.isEmpty()) {
       return res.status(400).json({ errors: errors.array() });
}
const userid = req.params.userid
//Setup or Edit
var site = req.body.site
var adminpwd = req.body.admin
var cpus = req.body.cpus
var memory = req.body.memory
var storage = req.body.storage
//Needs a function to get the next available port
var dockerPort = await freePort()
console.log(dockerPort)
//Cutting https:// from input
var site_name = site.slice(8)
//Save or Edit in DB
Users.updateOne({_id: userid}, {docker: {site: site, adminpwd: adminpwd, hostPort: dockerPort, cpus: cpus, memory: memory, storage: storage}}, {upsert: true, new: true},
  function (err, result) {
  if (err) throw err
  console.log('Docker Parameters Setup in User' + result)
})
await ansible.createGlobal(site_name, adminpwd, dockerPort, cpus, memory, storage)
await res.redirect(userid+'/output')
})

As always since I'm still quite new to development, if anyone has any better options or ways to actually implement a live stream of the spawnSync command, I would really appreciate learning those techniques or logic.

Firdaus Aga
  • 27
  • 2
  • 6