2021-02-26 12:46:48 +01:00
const Command = require ( "../structures/Command" )
2021-03-30 11:50:42 +02:00
const { CommandWords , InitTypes , AnalyticsEvents } = require ( "../constants" )
2021-02-26 14:30:24 +01:00
const { lookpath } = require ( "lookpath" )
2021-03-19 11:29:43 +01:00
const {
downloadFile ,
logErrorToFile ,
success ,
info ,
parseEnv ,
} = require ( "../utils" )
2021-02-26 14:30:24 +01:00
const { confirmation } = require ( "../questions" )
2021-02-26 16:09:25 +01:00
const fs = require ( "fs" )
const compose = require ( "docker-compose" )
2021-03-19 11:29:43 +01:00
const makeEnv = require ( "./makeEnv" )
2021-03-19 11:02:29 +01:00
const axios = require ( "axios" )
2021-03-30 11:50:42 +02:00
const AnalyticsClient = require ( "../analytics/Client" )
2021-02-26 14:30:24 +01:00
2021-02-26 16:09:25 +01:00
const BUDIBASE _SERVICES = [ "app-service" , "worker-service" ]
const ERROR _FILE = "docker-error.log"
2021-02-26 14:30:24 +01:00
const FILE _URLS = [
"https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml" ,
2021-02-26 18:09:20 +01:00
"https://raw.githubusercontent.com/Budibase/budibase/master/hosting/envoy.yaml" ,
2021-02-26 14:30:24 +01:00
]
2021-03-19 11:02:29 +01:00
const DO _USER _DATA _URL = "http://169.254.169.254/metadata/v1/user-data"
2021-02-26 14:30:24 +01:00
2021-03-30 11:50:42 +02:00
const client = new AnalyticsClient ( )
2021-02-26 18:08:28 +01:00
async function downloadFiles ( ) {
const promises = [ ]
for ( let url of FILE _URLS ) {
const fileName = url . split ( "/" ) . slice ( - 1 ) [ 0 ]
promises . push ( downloadFile ( url , ` ./ ${ fileName } ` ) )
}
await Promise . all ( promises )
}
2021-02-26 14:30:24 +01:00
async function checkDockerConfigured ( ) {
2021-02-26 18:09:20 +01:00
const error =
"docker/docker-compose has not been installed, please follow instructions at: https://docs.budibase.com/self-hosting/hosting-methods/docker-compose#installing-docker"
2021-02-26 14:30:24 +01:00
const docker = await lookpath ( "docker" )
const compose = await lookpath ( "docker-compose" )
if ( ! docker || ! compose ) {
throw error
}
}
2021-02-24 18:32:45 +01:00
2021-02-26 16:09:25 +01:00
function checkInitComplete ( ) {
2021-03-19 11:29:43 +01:00
if ( ! fs . existsSync ( makeEnv . filePath ) ) {
2021-02-26 16:09:25 +01:00
throw "Please run the hosting --init command before any other hosting command."
}
}
async function handleError ( func ) {
try {
await func ( )
} catch ( err ) {
if ( err && err . err ) {
logErrorToFile ( ERROR _FILE , err . err )
}
throw ` Failed to start - logs written to file: ${ ERROR _FILE } `
}
}
2021-03-19 11:02:29 +01:00
async function init ( type ) {
const isQuick = type === InitTypes . QUICK || type === InitTypes . DIGITAL _OCEAN
2021-02-26 14:30:24 +01:00
await checkDockerConfigured ( )
2021-03-18 19:26:41 +01:00
if ( ! isQuick ) {
const shouldContinue = await confirmation (
"This will create multiple files in current directory, should continue?"
)
if ( ! shouldContinue ) {
console . log ( "Stopping." )
return
}
2021-02-26 14:30:24 +01:00
}
2021-03-30 11:50:42 +02:00
client . capture ( {
2021-03-30 12:04:13 +02:00
distinctId : "cli" ,
2021-03-30 11:50:42 +02:00
event : AnalyticsEvents . SelfHostInit ,
properties : {
2021-03-30 12:50:49 +02:00
type ,
} ,
2021-03-30 11:50:42 +02:00
} )
2021-02-26 18:08:28 +01:00
await downloadFiles ( )
2021-03-19 11:29:43 +01:00
const config = isQuick ? makeEnv . QUICK _CONFIG : { }
2021-03-19 11:02:29 +01:00
if ( type === InitTypes . DIGITAL _OCEAN ) {
try {
2021-03-19 11:29:43 +01:00
const output = await axios . get ( DO _USER _DATA _URL )
const response = parseEnv ( output . data )
for ( let [ key , value ] of Object . entries ( makeEnv . ConfigMap ) ) {
2021-03-19 11:02:29 +01:00
if ( response [ key ] ) {
config [ value ] = response [ key ]
}
}
} catch ( err ) {
// don't need to handle error, just don't do anything
}
}
2021-03-19 11:29:43 +01:00
await makeEnv . make ( config )
2021-02-26 12:46:48 +01:00
}
async function start ( ) {
2021-02-26 16:09:25 +01:00
await checkDockerConfigured ( )
checkInitComplete ( )
2021-05-24 17:20:28 +02:00
console . log (
info (
"Starting services, this may take a moment - first time this may take a few minutes to download images."
)
)
2021-03-19 11:29:43 +01:00
const port = makeEnv . get ( "MAIN_PORT" )
2021-02-26 16:09:25 +01:00
await handleError ( async ( ) => {
2021-05-24 15:58:54 +02:00
// need to log as it makes it more clear
await compose . upAll ( { cwd : "./" , log : true } )
2021-02-26 16:09:25 +01:00
} )
2021-02-26 18:09:20 +01:00
console . log (
success (
` Services started, please go to http://localhost: ${ port } for next steps. `
)
)
2021-02-26 16:09:25 +01:00
}
async function status ( ) {
await checkDockerConfigured ( )
checkInitComplete ( )
2021-02-26 18:08:28 +01:00
console . log ( info ( "Budibase status" ) )
2021-02-26 16:09:25 +01:00
await handleError ( async ( ) => {
const response = await compose . ps ( )
console . log ( response . out )
} )
2021-02-26 12:46:48 +01:00
}
async function stop ( ) {
2021-02-26 16:09:25 +01:00
await checkDockerConfigured ( )
checkInitComplete ( )
2021-02-26 18:08:28 +01:00
console . log ( info ( "Stopping services, this may take a moment." ) )
2021-02-26 16:09:25 +01:00
await handleError ( async ( ) => {
await compose . stop ( )
} )
2021-02-26 18:08:28 +01:00
console . log ( success ( "Services have been stopped successfully." ) )
2021-02-26 12:46:48 +01:00
}
async function update ( ) {
2021-02-26 16:09:25 +01:00
await checkDockerConfigured ( )
checkInitComplete ( )
2021-02-26 18:09:20 +01:00
if (
await confirmation (
"Do you wish to update you docker-compose.yaml and envoy.yaml?"
)
) {
2021-02-26 18:08:28 +01:00
await downloadFiles ( )
}
2021-02-26 16:09:25 +01:00
await handleError ( async ( ) => {
const status = await compose . ps ( )
const parts = status . out . split ( "\n" )
const isUp = parts [ 2 ] && parts [ 2 ] . indexOf ( "Up" ) !== - 1
2021-02-26 18:08:28 +01:00
if ( isUp ) {
console . log ( info ( "Stopping services, this may take a moment." ) )
await compose . stop ( )
}
console . log ( info ( "Beginning update, this may take a few minutes." ) )
2021-02-26 18:09:20 +01:00
await compose . pullMany ( BUDIBASE _SERVICES , { log : true } )
2021-02-26 16:09:25 +01:00
if ( isUp ) {
2021-02-26 18:08:28 +01:00
console . log ( success ( "Update complete, restarting services..." ) )
2021-02-26 16:09:25 +01:00
await start ( )
}
} )
2021-02-26 12:46:48 +01:00
}
const command = new Command ( ` ${ CommandWords . HOSTING } ` )
2021-02-25 15:42:50 +01:00
. addHelp ( "Controls self hosting on the Budibase platform." )
2021-02-26 12:46:48 +01:00
. addSubOption (
2021-03-18 19:26:41 +01:00
"--init [type]" ,
"Configure a self hosted platform in current directory, type can be unspecified or 'quick'." ,
2021-02-26 12:46:48 +01:00
init
)
. addSubOption (
"--start" ,
"Start the configured platform in current directory." ,
start
)
2021-02-26 16:09:25 +01:00
. addSubOption (
"--status" ,
"Check the status of currently running services." ,
status
)
2021-02-26 12:46:48 +01:00
. addSubOption (
"--stop" ,
"Stop the configured platform in the current directory." ,
stop
)
. addSubOption (
"--update" ,
2021-03-01 19:04:30 +01:00
"Update the Budibase images to the latest version." ,
2021-02-26 12:46:48 +01:00
update
)
2021-02-25 15:42:50 +01:00
2021-02-26 12:46:48 +01:00
exports . command = command