mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-19 13:47:54 +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
|
||||
}
|
||||
|
||||
type LogToDisplayBody struct {
|
||||
Enable bool
|
||||
}
|
||||
|
||||
func jsonResponse(writer http.ResponseWriter, response interface{}, status int) {
|
||||
jsonBody, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
@ -67,12 +71,6 @@ func parsePurgeBody(request *http.Request) (appId string, err error) {
|
||||
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)) {
|
||||
sendResponse = func(data interface{}, errorMsg string, statusCode int) {
|
||||
jsonResponse(writer, APIResponse{data, errorMsg}, statusCode)
|
||||
@ -221,3 +219,52 @@ func VPNControl(writer http.ResponseWriter, request *http.Request) {
|
||||
log.Printf("%sd\n", actionDescr)
|
||||
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("/shutdown", ShutdownHandler).Methods("POST")
|
||||
apiv1.HandleFunc("/vpncontrol", VPNControl).Methods("POST")
|
||||
apiv1.HandleFunc("/set-log-to-display", LogToDisplayControl).Methods("POST")
|
||||
}
|
||||
|
||||
func startApi(listenAddress string, router *mux.Router) {
|
||||
|
@ -84,6 +84,19 @@ logSystemEvent = (logType, app, error) ->
|
||||
utils.mixpanelTrack(logType.eventName, {app, error})
|
||||
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.kill = kill = (app, updateDB = true) ->
|
||||
@ -340,12 +353,14 @@ executeSpecialActionsAndHostConfig = (env) ->
|
||||
if env[key]? && specialActionCallback?
|
||||
# This makes the Special Action Envs only trigger their functions once.
|
||||
if !_.has(executedSpecialActionEnvVars, key) or executedSpecialActionEnvVars[key] != env[key]
|
||||
logSpecialAction(key, env[key])
|
||||
specialActionCallback(env[key])
|
||||
executedSpecialActionEnvVars[key] = env[key]
|
||||
logSpecialAction(key, env[key], true)
|
||||
hostConfigVars = _.pick env, (val, key) ->
|
||||
return _.startsWith(key, device.hostConfigEnvVarPrefix)
|
||||
if !_.isEmpty(hostConfigVars)
|
||||
device.setHostConfig(hostConfigVars)
|
||||
device.setHostConfig(hostConfigVars, logMessage)
|
||||
|
||||
wrapAsError = (err) ->
|
||||
return err if _.isError(err)
|
||||
@ -460,6 +475,8 @@ getEnvAndFormatRemoteApps = (deviceId, remoteApps, uuid, apiKey) ->
|
||||
utils.extendEnvVars(app.environment_variable, uuid)
|
||||
.then (fullEnv) ->
|
||||
env = _.omit(fullEnv, _.keys(specialActionEnvVars))
|
||||
env = _.omitBy env, (v, k) ->
|
||||
_.startsWith(k, device.hostConfigEnvVarPrefix)
|
||||
return [
|
||||
{
|
||||
appId: '' + app.id
|
||||
@ -528,7 +545,7 @@ application.update = update = (force) ->
|
||||
Promise.map appsWithChangedEnvs, (appId) ->
|
||||
Promise.using lockUpdates(remoteApps[appId], force), ->
|
||||
executeSpecialActionsAndHostConfig(remoteAppEnvs[appId])
|
||||
.then ->
|
||||
.tap ->
|
||||
# If an env var shouldn't cause a restart but requires an action, we should still
|
||||
# save the new env to the DB
|
||||
if !_.includes(toBeUpdated, appId) and !_.includes(toBeInstalled, appId)
|
||||
@ -538,6 +555,8 @@ application.update = update = (force) ->
|
||||
throw new Error('App not found')
|
||||
app.env = JSON.stringify(remoteAppEnvs[appId])
|
||||
knex('app').update(app).where({ appId })
|
||||
.then (needsReboot) ->
|
||||
device.reboot() if needsReboot
|
||||
.catch (err) ->
|
||||
logSystemEvent(logTypes.updateAppError, remoteApps[appId], err)
|
||||
.return(allAppIds)
|
||||
|
@ -35,7 +35,7 @@ exports.getID = do ->
|
||||
throw new Error('Could not find this device?!')
|
||||
return devices[0].id
|
||||
|
||||
rebootDevice = ->
|
||||
exports.reboot = rebootDevice = ->
|
||||
request.postAsync(config.gosuperAddress + '/v1/reboot')
|
||||
|
||||
exports.hostConfigEnvVarPrefix = hostConfigEnvVarPrefix = 'RESIN_HOST_'
|
||||
@ -70,26 +70,30 @@ parseBootConfigFromEnv = (env) ->
|
||||
parsedEnv = _.omit(parsedEnv, forbiddenConfigKeys)
|
||||
return parsedEnv
|
||||
|
||||
exports.setHostConfig = (env) ->
|
||||
Promise.join setBootConfig(env), setLogToDisplay(env), (bootConfigApplied, logToDisplayChanged) ->
|
||||
rebootDevice() if bootConfigApplied or logToDisplayChanged
|
||||
exports.setHostConfig = (env, logMessage) ->
|
||||
Promise.join setBootConfig(env, logMessage), setLogToDisplay(env, logMessage), (bootConfigApplied, logToDisplayChanged) ->
|
||||
return true if bootConfigApplied or logToDisplayChanged
|
||||
return false
|
||||
|
||||
setLogToDisplay = (env) ->
|
||||
setLogToDisplay = (env, logMessage) ->
|
||||
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) ->
|
||||
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
|
||||
else
|
||||
if body.Data == true
|
||||
logMessage("#{enable ? "Enabled" : "Disabled"} logs to display")
|
||||
return body.Data
|
||||
else
|
||||
return false
|
||||
|
||||
setBootConfig = (env) ->
|
||||
setBootConfig = (env, logMessage) ->
|
||||
device.getDeviceType()
|
||||
.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 ) ->
|
||||
throw new Error('No boot config to change') if _.isEmpty(configFromApp)
|
||||
configFromFS = {}
|
||||
@ -111,6 +115,8 @@ setBootConfig = (env) ->
|
||||
toBeChanged = _.filter toBeChanged, (key) ->
|
||||
configFromApp[key] != configFromFS[key]
|
||||
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
|
||||
outputConfig = _.map toBeAdded, (key) -> "#{key}=#{configFromApp[key]}"
|
||||
outputConfig = outputConfig.concat _.map configPositions, (key, index) ->
|
||||
@ -129,7 +135,11 @@ setBootConfig = (env) ->
|
||||
.then ->
|
||||
execAsync('sync')
|
||||
.then ->
|
||||
logMessage("Applied boot config: #{configFromApp}")
|
||||
return true
|
||||
.catch (err) ->
|
||||
logMessage("Error setting boot config: #{err}")
|
||||
throw err
|
||||
.catch (err) ->
|
||||
console.log('Will not set boot config: ', err)
|
||||
return false
|
||||
|
Loading…
Reference in New Issue
Block a user