mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(agent): add script data dir for binaries and files (#12205)
The agent is extended with a `--script-data-dir` flag, defaulting to the OS temp dir. This dir is used for storing `coder-script-data/bin` and `coder-script/[script uuid]`. The former is a place for all scripts to place executable binaries that will be available by other scripts, SSH sessions, etc. The latter is a place for the script to store files. Since we default to OS temp dir, files are ephemeral by default. In the future, we may consider adding new env vars or changing the default storage location. Workspace startup speed could potentially benefit from scripts being able to skip steps that require downloading software. We may also extend this with more env variables (e.g. persistent storage in HOME). Fixes #11131
This commit is contained in:
@@ -43,11 +43,12 @@ var (
|
||||
|
||||
// Options are a set of options for the runner.
|
||||
type Options struct {
|
||||
LogDir string
|
||||
Logger slog.Logger
|
||||
SSHServer *agentssh.Server
|
||||
Filesystem afero.Fs
|
||||
PatchLogs func(ctx context.Context, req agentsdk.PatchLogs) error
|
||||
DataDirBase string
|
||||
LogDir string
|
||||
Logger slog.Logger
|
||||
SSHServer *agentssh.Server
|
||||
Filesystem afero.Fs
|
||||
PatchLogs func(ctx context.Context, req agentsdk.PatchLogs) error
|
||||
}
|
||||
|
||||
// New creates a runner for the provided scripts.
|
||||
@@ -59,6 +60,7 @@ func New(opts Options) *Runner {
|
||||
cronCtxCancel: cronCtxCancel,
|
||||
cron: cron.New(cron.WithParser(parser)),
|
||||
closed: make(chan struct{}),
|
||||
dataDir: filepath.Join(opts.DataDirBase, "coder-script-data"),
|
||||
scriptsExecuted: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "agent",
|
||||
Subsystem: "scripts",
|
||||
@@ -78,6 +80,7 @@ type Runner struct {
|
||||
cron *cron.Cron
|
||||
initialized atomic.Bool
|
||||
scripts []codersdk.WorkspaceAgentScript
|
||||
dataDir string
|
||||
|
||||
// scriptsExecuted includes all scripts executed by the workspace agent. Agents
|
||||
// execute startup scripts, and scripts on a cron schedule. Both will increment
|
||||
@@ -85,6 +88,17 @@ type Runner struct {
|
||||
scriptsExecuted *prometheus.CounterVec
|
||||
}
|
||||
|
||||
// DataDir returns the directory where scripts data is stored.
|
||||
func (r *Runner) DataDir() string {
|
||||
return r.dataDir
|
||||
}
|
||||
|
||||
// ScriptBinDir returns the directory where scripts can store executable
|
||||
// binaries.
|
||||
func (r *Runner) ScriptBinDir() string {
|
||||
return filepath.Join(r.dataDir, "bin")
|
||||
}
|
||||
|
||||
func (r *Runner) RegisterMetrics(reg prometheus.Registerer) {
|
||||
if reg == nil {
|
||||
// If no registry, do nothing.
|
||||
@@ -104,6 +118,11 @@ func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error {
|
||||
r.scripts = scripts
|
||||
r.Logger.Info(r.cronCtx, "initializing agent scripts", slog.F("script_count", len(scripts)), slog.F("log_dir", r.LogDir))
|
||||
|
||||
err := r.Filesystem.MkdirAll(r.ScriptBinDir(), 0o700)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create script bin dir: %w", err)
|
||||
}
|
||||
|
||||
for _, script := range scripts {
|
||||
if script.Cron == "" {
|
||||
continue
|
||||
@@ -208,7 +227,18 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript)
|
||||
if !filepath.IsAbs(logPath) {
|
||||
logPath = filepath.Join(r.LogDir, logPath)
|
||||
}
|
||||
logger := r.Logger.With(slog.F("log_path", logPath))
|
||||
|
||||
scriptDataDir := filepath.Join(r.DataDir(), script.LogSourceID.String())
|
||||
err := r.Filesystem.MkdirAll(scriptDataDir, 0o700)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("%s script: create script temp dir: %w", scriptDataDir, err)
|
||||
}
|
||||
|
||||
logger := r.Logger.With(
|
||||
slog.F("log_source_id", script.LogSourceID),
|
||||
slog.F("log_path", logPath),
|
||||
slog.F("script_data_dir", scriptDataDir),
|
||||
)
|
||||
logger.Info(ctx, "running agent script", slog.F("script", script.Script))
|
||||
|
||||
fileWriter, err := r.Filesystem.OpenFile(logPath, os.O_CREATE|os.O_RDWR, 0o600)
|
||||
@@ -238,6 +268,13 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript)
|
||||
cmd.WaitDelay = 10 * time.Second
|
||||
cmd.Cancel = cmdCancel(cmd)
|
||||
|
||||
// Expose env vars that can be used in the script for storing data
|
||||
// and binaries. In the future, we may want to expose more env vars
|
||||
// for the script to use, like CODER_SCRIPT_DATA_DIR for persistent
|
||||
// storage.
|
||||
cmd.Env = append(cmd.Env, "CODER_SCRIPT_DATA_DIR="+scriptDataDir)
|
||||
cmd.Env = append(cmd.Env, "CODER_SCRIPT_BIN_DIR="+r.ScriptBinDir())
|
||||
|
||||
send, flushAndClose := agentsdk.LogsSender(script.LogSourceID, r.PatchLogs, logger)
|
||||
// If ctx is canceled here (or in a writer below), we may be
|
||||
// discarding logs, but that's okay because we're shutting down
|
||||
|
||||
Reference in New Issue
Block a user