Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ bin/fioconfig-%: FORCE
GOARCH=$(shell echo $* | cut -f2 -d\-) \
go build -tags vpn $(LDFLAGS) -o $@ main.go

.PHONY: bin/fioconfig-nopkcs11
bin/fioconfig-nopkcs11:
CGO_ENABLED=0 go build -tags vpn,disable_pkcs11 $(LDFLAGS) -o $@ main.go

Expand Down
4 changes: 4 additions & 0 deletions contrib/actions/diag
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh -ex

/usr/sbin/fio-diag.sh
dmesg > $ARTIFACTS/dmesg.log
5 changes: 5 additions & 0 deletions contrib/actions/reboot
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh -ex

# Schedule a reboot in one minute to give fioconfig the chance to create a
# TestResult on the backend so the operator will know the device is rebooting.
systemctl reboot --when="+5 seconds"
63 changes: 63 additions & 0 deletions contrib/fioconfig-oneshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/sh -e

# This is an OnChanged handler that can handle requests to run a command
# one time, capture the output, and report it back to the device-gateway.

[ -z "${CONFIG_FILE}" ] && (echo "No CONFIG_FILE specified"; exit 1)
[ -f "${CONFIG_FILE}" ] || (echo "${CONFIG_FILE} does not exist. Assuming config file was deleted"; exit 0)

ACTIONS_DIR=${ACTIONS_DIR-"/usr/share/fioconfig/actions"}
[ -d "${ACTIONS_DIR}" ] || (echo "${ACTIONS_DIR} does not exist"; exit 1)

cmd_id=$(cat ${CONFIG_FILE} | grep COMMAND_ID | cut -d= -f2)
[ -z "${cmd_id}" ] && (echo "No COMMAND_ID found in config file"; exit 1)
echo "Command ID is ${cmd_id}"

capture=$(cat ${CONFIG_FILE} | grep COMMAND_CAPTURE | cut -d= -f2)

action=$1
active_file="${STORAGE_DIR}/oneshot.${action}.state"
completed_file="${active_file}.completed"

echo "Action is ${action}"
cmd="${ACTIONS_DIR}/${action}"
if [ -x "${cmd}" ] ; then
if [ "${capture}" = "1" ]; then
artifacts_dir=$(mktemp -d fioconfig-oneshot.XXXXXX)
trap 'rm -rf ${artifacts_dir}' EXIT

echo "Capturing stdout/stderr"
cmd="${FIOCONFIG_BIN} run-and-report --artifacts-dir ${artifacts_dir} --id ${cmd_id} --name ${action} ${cmd}"
fi
else
echo "Action not found in ${ACTIONS_DIR} or is not executable"
echo -n "${cmd_id}" > ${completed_file}
if [ "${capture}" = "1" ]; then
# report something back to the operator
${FIOCONFIG_BIN} run-and-report --id ${cmd_id} --name ${action} /bin/sh -c "echo Action(${cmd}) not found; exit 1"
fi
exit 1
fi

# We'll execute under two conditions:
# 1) We've never been run (oneshot.state.completed does not exist)
# 3) We have a new command ($cmd_id != oneshot.state.completed's COMMANDID)

runcmd() {
${cmd} ; echo -n "${cmd_id}" > ${completed_file}
rm -rf ${artifacts_dir}
exit
}

if [ ! -f "${completed_file}" ] ; then
echo "Completion file, ${completed_file}, does not exist"
runcmd
fi

completed_id=$(cat ${completed_file})
if [ "${completed_id}" != "${cmd_id}" ] ; then
echo "Running command: COMMAND_ID has changed from ${completed_id} -> ${cmd_id}"
runcmd
fi

echo "Command not needed. Current COMMAND_ID is ${cmd_id}"
65 changes: 65 additions & 0 deletions internal/remote_actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//go:build !disable_remoteactions

package internal

import (
"bytes"
"errors"
"log/slog"
"os"
"path/filepath"
"strings"
)

type raInitCallback struct {
newActions []byte
}

func (r *raInitCallback) ConfigFiles(app *App) []ConfigFileReq {
const actionsDir = "/usr/share/fioconfig/actions"
var actions []string
entries, err := os.ReadDir(actionsDir)
if err != nil && !errors.Is(err, os.ErrNotExist) {
// This is a bug - we can't read the directory for some reason
slog.Error("Unable to initialize remote actions", "error", err)
return nil
} else if err == nil {
for _, entry := range entries {
actions = append(actions, entry.Name())
}
}
actionsStr := strings.Join(actions, ",")

content, err := os.ReadFile(filepath.Join(app.SecretsDir, "fio-remote-actions"))
if err != nil {
if os.IsNotExist(err) {
content = nil
} else {
slog.Warn("Unable to read configured remote actions", "error", err)
return nil
}
}

if !bytes.Equal(content, []byte(actionsStr)) {
slog.Info("Configured remote actions changed", "old", string(content), "new", actionsStr)
file := ConfigFileReq{
Name: "fio-remote-actions",
Unencrypted: true,
Value: actionsStr,
}
r.newActions = []byte(actionsStr)
return []ConfigFileReq{file}
}

// prevent init logic from calling OnComplete by removing ourselves
delete(initCallbacks, "remote-actions")
return nil
}

func (r raInitCallback) OnComplete(app *App) {
delete(initCallbacks, "remote-actions")
}

func init() {
initCallbacks["remote-actions"] = &raInitCallback{}
}
Loading