|
|
@@ -0,0 +1,105 @@
|
|
|
+const PortainerClient = require('portainer-api-client')
|
|
|
+const fs = require('fs')
|
|
|
+const figlet = require('figlet')
|
|
|
+
|
|
|
+const TYPES = {
|
|
|
+ swarm: 1,
|
|
|
+ compose: 2
|
|
|
+}
|
|
|
+
|
|
|
+const createClient = (config) => {
|
|
|
+ config = Object.assign({
|
|
|
+ url: null,
|
|
|
+ username: null,
|
|
|
+ password: null
|
|
|
+ }, config)
|
|
|
+ const warning = `# ${figlet.textSync('WARNING!', 'colossal').split('\n').join('\n# ')}\n# This stack was deployed automatically. Any changes made here may be overwritten.\n#\n\n`
|
|
|
+
|
|
|
+ const client = new PortainerClient(config.url, config.username, config.password);
|
|
|
+
|
|
|
+ const call = async (method, path, data) => {
|
|
|
+ if (config.verbose) console.info(method, path, data ? JSON.stringify(data, null, 4) : '')
|
|
|
+ const result = await client.callApiWithKey(method, path, data)
|
|
|
+ if (config.verbose) console.info(JSON.stringify(result, null, 4))
|
|
|
+ return result
|
|
|
+ }
|
|
|
+
|
|
|
+ const getEndpointSwarm = async (endpointId) => await call('get', `/api/endpoints/${encodeURIComponent(endpointId)}/docker/swarm`)
|
|
|
+ const getEndpoints = async () => await call('get', '/api/endpoints')
|
|
|
+ const getSwarms = async () => await call('get', '/api/swarms')
|
|
|
+ const getStacks = async () => await call('get', '/api/stacks')
|
|
|
+ const deleteStack = async (opts) => {
|
|
|
+ opts = Object.assign({
|
|
|
+ name: null,
|
|
|
+ id: null,
|
|
|
+ endpointId: null
|
|
|
+ }, opts)
|
|
|
+ if (!opts.id) {
|
|
|
+ if (!opts.name) {
|
|
|
+ throw new Error('Either name or id required')
|
|
|
+ }
|
|
|
+ const stacks = await getStacks()
|
|
|
+ const stack = stacks.find(x => x.Name === opts.name)
|
|
|
+ if (!stack) throw new Error(`Stack ${opts.name} not found.`)
|
|
|
+ opts.id = stack.Id
|
|
|
+ }
|
|
|
+ if (!opts.endpointId) {
|
|
|
+ const endpoints = await getEndpoints()
|
|
|
+ if (!endpoints || !endpoints.length) throw new Error('Could not find any endpoints')
|
|
|
+ opts.endpointId = endpoints[0].Id
|
|
|
+ }
|
|
|
+ console.log(`Deleting stack ${opts.id}`)
|
|
|
+ return call('delete', `/api/stacks/${encodeURIComponent(opts.id)}`)
|
|
|
+ }
|
|
|
+ const upsertStack = async (opts) => {
|
|
|
+ opts = Object.assign({
|
|
|
+ name: null,
|
|
|
+ stackFile: null,
|
|
|
+ stackFileContent: null,
|
|
|
+ swarmId: null,
|
|
|
+ endpointId: null,
|
|
|
+ type: 'swarm',
|
|
|
+ method: 'string',
|
|
|
+ prune: true,
|
|
|
+ warning: true
|
|
|
+ }, opts)
|
|
|
+ if (!opts.name) throw new Error('name required')
|
|
|
+ if (!opts.stackFileContent) {
|
|
|
+ if (!opts.stackFile) {
|
|
|
+ throw new Error('stackFile or stackFileContent required')
|
|
|
+ }
|
|
|
+ opts.stackFileContent = fs.readFileSync(opts.stackFile, 'utf-8')
|
|
|
+ }
|
|
|
+ const type = TYPES[opts.type] || opts.type
|
|
|
+ if (!opts.endpointId) {
|
|
|
+ const endpoints = await getEndpoints()
|
|
|
+ if (!endpoints || !endpoints.length) throw new Error('Could not find any endpoints')
|
|
|
+ opts.endpointId = endpoints[0].Id
|
|
|
+ }
|
|
|
+ if (!opts.swarmId) {
|
|
|
+ const swarm = await getEndpointSwarm(opts.endpointId)
|
|
|
+ opts.swarmId = swarm.ID
|
|
|
+ }
|
|
|
+ const stacks = await getStacks()
|
|
|
+ const stack = stacks.find(x => x.Name === opts.name)
|
|
|
+ if (stack) {
|
|
|
+ console.log(`Found existing stack ${opts.name}. Updating...`)
|
|
|
+ return await call('PUT', `/api/stacks/${encodeURIComponent(stack.Id)}?method=${encodeURIComponent(opts.method)}&type=${encodeURIComponent(type)}&endpointId=${encodeURIComponent(opts.endpointId)}`, {
|
|
|
+ Name: opts.name,
|
|
|
+ StackFileContent: (opts.warning ? warning : '') + opts.stackFileContent,
|
|
|
+ SwarmID: opts.swarmId,
|
|
|
+ Prune: opts.prune
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ console.log(`New stack ${opts.name}`)
|
|
|
+ return await call('POST', `/api/stacks?method=${encodeURIComponent(opts.method)}&type=${encodeURIComponent(type)}&endpointId=${encodeURIComponent(opts.endpointId)}`, {
|
|
|
+ Name: opts.name,
|
|
|
+ StackFileContent: (opts.warning ? warning : '') + opts.stackFileContent,
|
|
|
+ SwarmID: opts.swarmId
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return { getEndpoints, getSwarms, getStacks, upsertStack, deleteStack }
|
|
|
+}
|
|
|
+
|
|
|
+module.exports = { createClient }
|