run executes the provided script with the timeout. If the timeout is exceeded, the process is sent an interrupt signal. If the process does not exit after a few seconds, it is forcefully killed. This function immediately returns after a timeout, and does not wait for the process to exit.
(ctx context.Context, script codersdk.WorkspaceAgentScript, option ExecuteOption)
| 249 | // If the process does not exit after a few seconds, it is forcefully killed. |
| 250 | // This function immediately returns after a timeout, and does not wait for the process to exit. |
| 251 | func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript, option ExecuteOption) error { |
| 252 | logPath := script.LogPath |
| 253 | if logPath == "" { |
| 254 | logPath = fmt.Sprintf("coder-script-%s.log", script.LogSourceID) |
| 255 | } |
| 256 | if logPath[0] == '~' { |
| 257 | // First we check the environment. |
| 258 | homeDir, err := os.UserHomeDir() |
| 259 | if err != nil { |
| 260 | u, err := user.Current() |
| 261 | if err != nil { |
| 262 | return xerrors.Errorf("current user: %w", err) |
| 263 | } |
| 264 | homeDir = u.HomeDir |
| 265 | } |
| 266 | logPath = filepath.Join(homeDir, logPath[1:]) |
| 267 | } |
| 268 | logPath = os.ExpandEnv(logPath) |
| 269 | if !filepath.IsAbs(logPath) { |
| 270 | logPath = filepath.Join(r.LogDir, logPath) |
| 271 | } |
| 272 | |
| 273 | scriptDataDir := filepath.Join(r.DataDir(), script.LogSourceID.String()) |
| 274 | err := r.Filesystem.MkdirAll(scriptDataDir, 0o700) |
| 275 | if err != nil { |
| 276 | return xerrors.Errorf("%s script: create script temp dir: %w", scriptDataDir, err) |
| 277 | } |
| 278 | |
| 279 | logger := r.Logger.With( |
| 280 | slog.F("log_source_id", script.LogSourceID), |
| 281 | slog.F("log_path", logPath), |
| 282 | slog.F("script_data_dir", scriptDataDir), |
| 283 | ) |
| 284 | logger.Info(ctx, "running agent script", slog.F("script", script.Script)) |
| 285 | |
| 286 | fileWriter, err := r.Filesystem.OpenFile(logPath, os.O_CREATE|os.O_RDWR, 0o600) |
| 287 | if err != nil { |
| 288 | return xerrors.Errorf("open %s script log file: %w", logPath, err) |
| 289 | } |
| 290 | defer func() { |
| 291 | err := fileWriter.Close() |
| 292 | if err != nil { |
| 293 | logger.Warn(ctx, fmt.Sprintf("close %s script log file", logPath), slog.Error(err)) |
| 294 | } |
| 295 | }() |
| 296 | |
| 297 | var cmd *exec.Cmd |
| 298 | cmdCtx := ctx |
| 299 | if script.Timeout > 0 { |
| 300 | var ctxCancel context.CancelFunc |
| 301 | cmdCtx, ctxCancel = context.WithTimeout(ctx, script.Timeout) |
| 302 | defer ctxCancel() |
| 303 | } |
| 304 | cmdPty, err := r.SSHServer.CreateCommand(cmdCtx, script.Script, nil, nil) |
| 305 | if err != nil { |
| 306 | return xerrors.Errorf("%s script: create command: %w", logPath, err) |
| 307 | } |
| 308 | cmd = cmdPty.AsExec() |
no test coverage detected