portainer.js 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #!/usr/bin/env node
  2. const { createClient } = require('../lib/client')
  3. const getConfig = require('microservice-config')
  4. const passwordPrompt = require('password-prompt')
  5. const asTable = require('as-table')
  6. const Vorpal = require('vorpal')
  7. const main = async() => {
  8. const vorpal = new Vorpal()
  9. function log(...args) { vorpal.activeCommand.log(...args) }
  10. const fillModel = async model => {
  11. if (!model.options) model.options = {}
  12. model.options.username = model.options.username || process.env.PORTAINER_USERNAME
  13. if (!model.options.username) throw new Error('Username is required.')
  14. model.options.password = model.options.password || process.env.PORTAINER_PASSWORD || await passwordPrompt('Password: ', {method: 'hide'})
  15. if (!model.options.password) throw new Error('Password is required.')
  16. model.options.url = model.options.url || process.env.PORTAINER_URL
  17. if (!model.options.url) throw new Error('URL is required.')
  18. if (model.options.verbose) {
  19. log('options', JSON.stringify({
  20. ...model,
  21. options: {
  22. ...model.options,
  23. password: '******'
  24. }
  25. }, null, 4))
  26. }
  27. }
  28. const command = commandSpec => vorpal.command(commandSpec)
  29. .option('-v, --verbose', 'Enable verbose logging')
  30. .option('-u, --username <username>', 'Portainer username. Defaults to environment variable PORTAINER_USERNAME.')
  31. .option('-p, --password <password>', 'Portainer password. Defaults to environment variable PORTAINER_PASSWORD.')
  32. .option('-r, --url <url>', 'Portainer URL, such as http://server:9000. Defaults to environment variable PORTAINER_URL.')
  33. .option('-j, --json', 'Format JSON.')
  34. const action = handler => async function(model, cb) {
  35. try {
  36. await fillModel(model)
  37. const client = createClient({
  38. log,
  39. ...model.options
  40. })
  41. const result = await handler(client, model)
  42. if (result) {
  43. if (!model.options.json) {
  44. if (Array.isArray(result)) {
  45. log(asTable(result))
  46. } else {
  47. log(asTable([result]))
  48. }
  49. } else {
  50. log(JSON.stringify(result))
  51. }
  52. }
  53. cb()
  54. } catch (err) {
  55. console.warn(err.message || err)
  56. process.exit(1)
  57. }
  58. }
  59. command('stack deploy <name>', 'Creates or updates a stack.')
  60. .option('-f, --file <yaml_file>', 'Path to docker-compose.yml file. Defaults to ./docker-compose.yml.')
  61. .option('-P, --prune', 'Prune obsolete or orphaned containers. Defaults to true.')
  62. .option('-W, --warning', 'Add warning message to head of yaml. Defaults to true.')
  63. .action(action(async (client, model) => {
  64. if (model.options.prune === undefined) model.options.prune = true
  65. if (model.options.warning === undefined) model.options.warning = true
  66. if (model.options.file === undefined) model.options.file = 'docker-compose.yml'
  67. log('Upserting stack')
  68. return await client.upsertStack({
  69. stackFile: model.options.file,
  70. ...model,
  71. ...model.options
  72. })
  73. }))
  74. command('stack ls', 'List stacks')
  75. .action(action(async (client, model) => await client.getStacks()))
  76. command('stack rm <name>', 'Removes a stack')
  77. .action(action(async (client, model) => await client.deleteStack(model)))
  78. command('endpoint ls', 'List endpoints')
  79. .action(action(async (client, model) => await client.getEndpoints()))
  80. await vorpal.parse(process.argv).catch(err => {
  81. console.error(err)
  82. process.exit(1)
  83. })
  84. }
  85. main().catch(err => {
  86. console.error(err)
  87. process.exit(1)
  88. })