diff --git a/cmd/orchestrator/main.go b/cmd/orchestrator/main.go index d1abb14..e665e24 100644 --- a/cmd/orchestrator/main.go +++ b/cmd/orchestrator/main.go @@ -19,6 +19,7 @@ import ( "github.com/circleci/runner-init/cmd/setup" initialize "github.com/circleci/runner-init/init" "github.com/circleci/runner-init/task" + "github.com/circleci/runner-init/task/entrypoint" "github.com/circleci/runner-init/task/taskerrors" ) @@ -39,6 +40,7 @@ type initCmd struct { type runTaskCmd struct { TerminationGracePeriod time.Duration `default:"10s" help:"How long the agent will wait for the task to complete if interrupted."` HealthCheckAddr string `default:":7623" help:"Address for the health check API to listen on."` + EntrypointOverride []string `hidden:"true"` // Task environment configuration should be injected through a Kubernetes Secret Config task.Config `required:"" hidden:"-"` @@ -102,9 +104,13 @@ func run(version, date string) (err error) { return sys.Run(ctx, cli.ShutdownDelay) } -func runSetup(ctx context.Context, cli cli, version string, sys *system.System) (*task.Orchestrator, error) { +func runSetup(ctx context.Context, cli cli, version string, sys *system.System) (Runner, error) { c := cli.RunTask + if len(c.EntrypointOverride) > 0 && os.Getpid() == 1 { + return entrypoint.New(c.EntrypointOverride), nil + } + // Strip the orchestrator configuration from the environment _ = os.Unsetenv("CIRCLECI_GOAT_CONFIG") @@ -130,3 +136,7 @@ func runSetup(ctx context.Context, cli cli, version string, sys *system.System) return o, nil } + +type Runner interface { + Run(ctx context.Context) error +} diff --git a/task/cmd/cmd.go b/task/cmd/cmd.go index a63c802..dafae57 100644 --- a/task/cmd/cmd.go +++ b/task/cmd/cmd.go @@ -10,7 +10,6 @@ import ( "os/signal" "os/user" "strconv" - "strings" "sync/atomic" "syscall" @@ -106,17 +105,7 @@ func newCmd(ctx context.Context, argv []string, user string, stderrSaver *prefix //#nosec:G204 // this is intentionally setting up a command cmd := exec.CommandContext(ctx, argv[0], argv[1:]...) - for _, env := range os.Environ() { - if strings.HasPrefix(env, "CIRCLECI_GOAT") { - // Prevent internal configuration from being injected in the command environment - continue - } - cmd.Env = append(cmd.Env, env) - } - if env != nil { - cmd.Env = append(cmd.Env, env...) - } - + cmd.Env = Environ(env...) cmd.Stdout = os.Stdout cmd.Stderr = io.MultiWriter(os.Stderr, stderrSaver) diff --git a/task/cmd/environ.go b/task/cmd/environ.go new file mode 100644 index 0000000..8ab5241 --- /dev/null +++ b/task/cmd/environ.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "os" + "strings" +) + +func Environ(extraEnv ...string) (environ []string) { + for _, env := range os.Environ() { + if strings.HasPrefix(env, "CIRCLECI_GOAT") { + // Prevent internal configuration from being unintentionally injected in the command environment + continue + } + environ = append(environ, env) + } + if extraEnv != nil { + environ = append(environ, extraEnv...) + } + + return environ +} diff --git a/task/entrypoint/entrypoint.go b/task/entrypoint/entrypoint.go new file mode 100644 index 0000000..7fa8fed --- /dev/null +++ b/task/entrypoint/entrypoint.go @@ -0,0 +1,35 @@ +package entrypoint + +import ( + "context" + "fmt" + "os" + "syscall" + + "github.com/circleci/ex/o11y" +) + +type Entrypoint struct { + args []string +} + +func New(args []string) Entrypoint { + return Entrypoint{args} +} + +func (e Entrypoint) Run(ctx context.Context) (err error) { + _, span := o11y.StartSpan(ctx, "override-entrypoint") + defer o11y.End(span, &err) + + args := os.Args + if len(e.args) > 1 { + args = append(e.args[1:], os.Args...) + } + + //#nosec:G204 // this is intentionally setting up a command + if err := syscall.Exec(e.args[0], args, os.Environ()); err != nil { + return fmt.Errorf("error execing entrypoint overide: %w", err) + } + + return nil +}