mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-05-07 03:18:12 +00:00
Finish log to display setting. Add logging to host config. Save host config before rebooting. Allow applying boot config to RPi3.
This commit is contained in:
parent
2c5bc8b90a
commit
eddc58ee86
@ -34,6 +34,10 @@ type VPNBody struct {
|
|||||||
Enable bool
|
Enable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LogToDisplayBody struct {
|
||||||
|
Enable bool
|
||||||
|
}
|
||||||
|
|
||||||
func jsonResponse(writer http.ResponseWriter, response interface{}, status int) {
|
func jsonResponse(writer http.ResponseWriter, response interface{}, status int) {
|
||||||
jsonBody, err := json.Marshal(response)
|
jsonBody, err := json.Marshal(response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -67,12 +71,6 @@ func parsePurgeBody(request *http.Request) (appId string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func responseSender(writer http.ResponseWriter) func(interface{}, string, int) {
|
|
||||||
return func(data interface{}, errorMsg string, statusCode int) {
|
|
||||||
jsonResponse(writer, APIResponse{data, errorMsg}, statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func responseSenders(writer http.ResponseWriter) (sendResponse func(interface{}, string, int), sendError func(error)) {
|
func responseSenders(writer http.ResponseWriter) (sendResponse func(interface{}, string, int), sendError func(error)) {
|
||||||
sendResponse = func(data interface{}, errorMsg string, statusCode int) {
|
sendResponse = func(data interface{}, errorMsg string, statusCode int) {
|
||||||
jsonResponse(writer, APIResponse{data, errorMsg}, statusCode)
|
jsonResponse(writer, APIResponse{data, errorMsg}, statusCode)
|
||||||
@ -221,3 +219,52 @@ func VPNControl(writer http.ResponseWriter, request *http.Request) {
|
|||||||
log.Printf("%sd\n", actionDescr)
|
log.Printf("%sd\n", actionDescr)
|
||||||
sendResponse("OK", "", http.StatusAccepted)
|
sendResponse("OK", "", http.StatusAccepted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//LogToDisplayControl is used to control tty-replacement service status with dbus
|
||||||
|
func LogToDisplayControl(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
sendResponse, sendError := responseSenders(writer)
|
||||||
|
serviceName := "tty-replacement.service"
|
||||||
|
var body LogToDisplayBody
|
||||||
|
if err := parseJSONBody(&body, request); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
sendResponse("Error", err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if systemd.Dbus == nil {
|
||||||
|
sendError(fmt.Errorf("Systemd dbus unavailable, cannot set log to display state."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if activeState, err := systemd.Dbus.GetUnitProperty(serviceName, "ActiveState"); err != nil {
|
||||||
|
sendError(fmt.Errorf("Unable to get log to display status: %v", err))
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
status := activeState.Value.String() == `"active"`
|
||||||
|
enable := body.Enable
|
||||||
|
if status == enable {
|
||||||
|
// Nothing to do, return Data = false to signal nothing was changed
|
||||||
|
sendResponse(false, "", http.StatusOK)
|
||||||
|
return
|
||||||
|
} else if enable {
|
||||||
|
if _, err := systemd.Dbus.StartUnit(serviceName, "fail", nil); err != nil {
|
||||||
|
sendError(fmt.Errorf("Unable to start service: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, _, err = systemd.Dbus.EnableUnitFiles([]string{serviceName}, false, false); err != nil {
|
||||||
|
sendError(fmt.Errorf("Unable to enable service: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := systemd.Dbus.StopUnit(serviceName, "fail", nil); err != nil {
|
||||||
|
sendError(fmt.Errorf("Unable to stop service: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = systemd.Dbus.DisableUnitFiles([]string{serviceName}, false); err != nil {
|
||||||
|
sendError(fmt.Errorf("Unable to disable service: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendResponse(true, "", http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,6 +25,7 @@ func setupApi(router *mux.Router) {
|
|||||||
apiv1.HandleFunc("/reboot", RebootHandler).Methods("POST")
|
apiv1.HandleFunc("/reboot", RebootHandler).Methods("POST")
|
||||||
apiv1.HandleFunc("/shutdown", ShutdownHandler).Methods("POST")
|
apiv1.HandleFunc("/shutdown", ShutdownHandler).Methods("POST")
|
||||||
apiv1.HandleFunc("/vpncontrol", VPNControl).Methods("POST")
|
apiv1.HandleFunc("/vpncontrol", VPNControl).Methods("POST")
|
||||||
|
apiv1.HandleFunc("/set-log-to-display", LogToDisplayControl).Methods("POST")
|
||||||
}
|
}
|
||||||
|
|
||||||
func startApi(listenAddress string, router *mux.Router) {
|
func startApi(listenAddress string, router *mux.Router) {
|
||||||
|
@ -84,6 +84,19 @@ logSystemEvent = (logType, app, error) ->
|
|||||||
utils.mixpanelTrack(logType.eventName, {app, error})
|
utils.mixpanelTrack(logType.eventName, {app, error})
|
||||||
return
|
return
|
||||||
|
|
||||||
|
logMessage = (msg) ->
|
||||||
|
logger.log(msg, isSystem: true)
|
||||||
|
utils.mixpanelTrack(msg)
|
||||||
|
|
||||||
|
logSpecialAction = (action, value, success) ->
|
||||||
|
if success
|
||||||
|
msg = "Applied config variable #{action} = #{value}"
|
||||||
|
else
|
||||||
|
msg = "Applying config variable #{action} = #{value}"
|
||||||
|
logMessage(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
application = {}
|
application = {}
|
||||||
|
|
||||||
application.kill = kill = (app, updateDB = true) ->
|
application.kill = kill = (app, updateDB = true) ->
|
||||||
@ -340,12 +353,14 @@ executeSpecialActionsAndHostConfig = (env) ->
|
|||||||
if env[key]? && specialActionCallback?
|
if env[key]? && specialActionCallback?
|
||||||
# This makes the Special Action Envs only trigger their functions once.
|
# This makes the Special Action Envs only trigger their functions once.
|
||||||
if !_.has(executedSpecialActionEnvVars, key) or executedSpecialActionEnvVars[key] != env[key]
|
if !_.has(executedSpecialActionEnvVars, key) or executedSpecialActionEnvVars[key] != env[key]
|
||||||
|
logSpecialAction(key, env[key])
|
||||||
specialActionCallback(env[key])
|
specialActionCallback(env[key])
|
||||||
executedSpecialActionEnvVars[key] = env[key]
|
executedSpecialActionEnvVars[key] = env[key]
|
||||||
|
logSpecialAction(key, env[key], true)
|
||||||
hostConfigVars = _.pick env, (val, key) ->
|
hostConfigVars = _.pick env, (val, key) ->
|
||||||
return _.startsWith(key, device.hostConfigEnvVarPrefix)
|
return _.startsWith(key, device.hostConfigEnvVarPrefix)
|
||||||
if !_.isEmpty(hostConfigVars)
|
if !_.isEmpty(hostConfigVars)
|
||||||
device.setHostConfig(hostConfigVars)
|
device.setHostConfig(hostConfigVars, logMessage)
|
||||||
|
|
||||||
wrapAsError = (err) ->
|
wrapAsError = (err) ->
|
||||||
return err if _.isError(err)
|
return err if _.isError(err)
|
||||||
@ -460,6 +475,8 @@ getEnvAndFormatRemoteApps = (deviceId, remoteApps, uuid, apiKey) ->
|
|||||||
utils.extendEnvVars(app.environment_variable, uuid)
|
utils.extendEnvVars(app.environment_variable, uuid)
|
||||||
.then (fullEnv) ->
|
.then (fullEnv) ->
|
||||||
env = _.omit(fullEnv, _.keys(specialActionEnvVars))
|
env = _.omit(fullEnv, _.keys(specialActionEnvVars))
|
||||||
|
env = _.omitBy env, (v, k) ->
|
||||||
|
_.startsWith(k, device.hostConfigEnvVarPrefix)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
appId: '' + app.id
|
appId: '' + app.id
|
||||||
@ -528,7 +545,7 @@ application.update = update = (force) ->
|
|||||||
Promise.map appsWithChangedEnvs, (appId) ->
|
Promise.map appsWithChangedEnvs, (appId) ->
|
||||||
Promise.using lockUpdates(remoteApps[appId], force), ->
|
Promise.using lockUpdates(remoteApps[appId], force), ->
|
||||||
executeSpecialActionsAndHostConfig(remoteAppEnvs[appId])
|
executeSpecialActionsAndHostConfig(remoteAppEnvs[appId])
|
||||||
.then ->
|
.tap ->
|
||||||
# If an env var shouldn't cause a restart but requires an action, we should still
|
# If an env var shouldn't cause a restart but requires an action, we should still
|
||||||
# save the new env to the DB
|
# save the new env to the DB
|
||||||
if !_.includes(toBeUpdated, appId) and !_.includes(toBeInstalled, appId)
|
if !_.includes(toBeUpdated, appId) and !_.includes(toBeInstalled, appId)
|
||||||
@ -538,6 +555,8 @@ application.update = update = (force) ->
|
|||||||
throw new Error('App not found')
|
throw new Error('App not found')
|
||||||
app.env = JSON.stringify(remoteAppEnvs[appId])
|
app.env = JSON.stringify(remoteAppEnvs[appId])
|
||||||
knex('app').update(app).where({ appId })
|
knex('app').update(app).where({ appId })
|
||||||
|
.then (needsReboot) ->
|
||||||
|
device.reboot() if needsReboot
|
||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
logSystemEvent(logTypes.updateAppError, remoteApps[appId], err)
|
logSystemEvent(logTypes.updateAppError, remoteApps[appId], err)
|
||||||
.return(allAppIds)
|
.return(allAppIds)
|
||||||
|
@ -35,7 +35,7 @@ exports.getID = do ->
|
|||||||
throw new Error('Could not find this device?!')
|
throw new Error('Could not find this device?!')
|
||||||
return devices[0].id
|
return devices[0].id
|
||||||
|
|
||||||
rebootDevice = ->
|
exports.reboot = rebootDevice = ->
|
||||||
request.postAsync(config.gosuperAddress + '/v1/reboot')
|
request.postAsync(config.gosuperAddress + '/v1/reboot')
|
||||||
|
|
||||||
exports.hostConfigEnvVarPrefix = hostConfigEnvVarPrefix = 'RESIN_HOST_'
|
exports.hostConfigEnvVarPrefix = hostConfigEnvVarPrefix = 'RESIN_HOST_'
|
||||||
@ -70,26 +70,30 @@ parseBootConfigFromEnv = (env) ->
|
|||||||
parsedEnv = _.omit(parsedEnv, forbiddenConfigKeys)
|
parsedEnv = _.omit(parsedEnv, forbiddenConfigKeys)
|
||||||
return parsedEnv
|
return parsedEnv
|
||||||
|
|
||||||
exports.setHostConfig = (env) ->
|
exports.setHostConfig = (env, logMessage) ->
|
||||||
Promise.join setBootConfig(env), setLogToDisplay(env), (bootConfigApplied, logToDisplayChanged) ->
|
Promise.join setBootConfig(env, logMessage), setLogToDisplay(env, logMessage), (bootConfigApplied, logToDisplayChanged) ->
|
||||||
rebootDevice() if bootConfigApplied or logToDisplayChanged
|
return true if bootConfigApplied or logToDisplayChanged
|
||||||
|
return false
|
||||||
|
|
||||||
setLogToDisplay = (env) ->
|
setLogToDisplay = (env, logMessage) ->
|
||||||
if env['RESIN_HOST_LOG_TO_DISPLAY']?
|
if env['RESIN_HOST_LOG_TO_DISPLAY']?
|
||||||
request.postAsync(config.gosuperAddress + '/v1/set-log-to-display')
|
enable = env['RESIN_HOST_LOG_TO_DISPLAY'] != '0'
|
||||||
|
request.postAsync(config.gosuperAddress + '/v1/set-log-to-display', {json: true, body: Enable: enable})
|
||||||
.spread (response, body) ->
|
.spread (response, body) ->
|
||||||
if response.statusCode != 200
|
if response.statusCode != 200
|
||||||
console.log('Error setting log to display:', body, "Status:", response.statusCode)
|
logMessage("Error setting log to display: #{body}, Status:, #{response.statusCode}")
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
|
if body.Data == true
|
||||||
|
logMessage("#{enable ? "Enabled" : "Disabled"} logs to display")
|
||||||
return body.Data
|
return body.Data
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
|
|
||||||
setBootConfig = (env) ->
|
setBootConfig = (env, logMessage) ->
|
||||||
device.getDeviceType()
|
device.getDeviceType()
|
||||||
.then (deviceType) ->
|
.then (deviceType) ->
|
||||||
throw new Error('This is not a Raspberry Pi') if !_.startsWith(deviceType, 'raspberry-pi')
|
throw new Error('This is not a Raspberry Pi') if !_.startsWith(deviceType, 'raspberry')
|
||||||
Promise.join parseBootConfigFromEnv(env), fs.readFileAsync(bootConfigPath, 'utf8'), (configFromApp, configTxt ) ->
|
Promise.join parseBootConfigFromEnv(env), fs.readFileAsync(bootConfigPath, 'utf8'), (configFromApp, configTxt ) ->
|
||||||
throw new Error('No boot config to change') if _.isEmpty(configFromApp)
|
throw new Error('No boot config to change') if _.isEmpty(configFromApp)
|
||||||
configFromFS = {}
|
configFromFS = {}
|
||||||
@ -111,6 +115,8 @@ setBootConfig = (env) ->
|
|||||||
toBeChanged = _.filter toBeChanged, (key) ->
|
toBeChanged = _.filter toBeChanged, (key) ->
|
||||||
configFromApp[key] != configFromFS[key]
|
configFromApp[key] != configFromFS[key]
|
||||||
throw new Error('Nothing to change') if _.isEmpty(toBeChanged) and _.isEmpty(toBeAdded)
|
throw new Error('Nothing to change') if _.isEmpty(toBeChanged) and _.isEmpty(toBeAdded)
|
||||||
|
|
||||||
|
logMessage("Applying boot config: #{configFromApp}")
|
||||||
# We add the keys to be added first so they are out of any filters
|
# We add the keys to be added first so they are out of any filters
|
||||||
outputConfig = _.map toBeAdded, (key) -> "#{key}=#{configFromApp[key]}"
|
outputConfig = _.map toBeAdded, (key) -> "#{key}=#{configFromApp[key]}"
|
||||||
outputConfig = outputConfig.concat _.map configPositions, (key, index) ->
|
outputConfig = outputConfig.concat _.map configPositions, (key, index) ->
|
||||||
@ -129,7 +135,11 @@ setBootConfig = (env) ->
|
|||||||
.then ->
|
.then ->
|
||||||
execAsync('sync')
|
execAsync('sync')
|
||||||
.then ->
|
.then ->
|
||||||
|
logMessage("Applied boot config: #{configFromApp}")
|
||||||
return true
|
return true
|
||||||
|
.catch (err) ->
|
||||||
|
logMessage("Error setting boot config: #{err}")
|
||||||
|
throw err
|
||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
console.log('Will not set boot config: ', err)
|
console.log('Will not set boot config: ', err)
|
||||||
return false
|
return false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user