From 2457d8d0d7049eebafd608ad428e23489f42f4a4 Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Wed, 11 Nov 2020 17:24:24 -0500 Subject: [PATCH] source tmate binary from docker image --- command/compile.go | 9 +++++++ command/daemon/config.go | 5 ++++ command/daemon/daemon.go | 4 ++++ command/exec.go | 9 +++++++ docker/Dockerfile.linux.amd64 | 10 ++++++-- docker/Dockerfile.linux.arm | 9 +++++-- docker/Dockerfile.linux.arm64 | 9 +++++-- engine/compiler/compiler.go | 44 +++++++++++++++++++++++++++++++++- engine/compiler/shell/shell.go | 19 ++------------- engine/engine.go | 34 ++++++++++++++++++++++++-- engine/spec.go | 1 + 11 files changed, 127 insertions(+), 26 deletions(-) diff --git a/command/compile.go b/command/compile.go index d9880b9..f406ee6 100644 --- a/command/compile.go +++ b/command/compile.go @@ -37,6 +37,7 @@ type compileCommand struct { Labels map[string]string Secrets map[string]string Resources compiler.Resources + Tmate compiler.Tmate Clone bool Config string } @@ -101,6 +102,7 @@ func (c *compileCommand) run(*kingpin.ParseContext) error { Environ: provider.Static(c.Environ), Labels: c.Labels, Resources: c.Resources, + Tmate: c.Tmate, Privileged: append(c.Privileged, compiler.Privileged...), Networks: c.Networks, Volumes: c.Volumes, @@ -192,6 +194,13 @@ func registerCompile(app *kingpin.Application) { cmd.Flag("docker-config", "path to the docker config file"). StringVar(&c.Config) + cmd.Flag("tmate-image", "tmate docker image"). + Default("drone/drone-runner-docker:latest"). + StringVar(&c.Tmate.Image) + + cmd.Flag("tmate-enabled", "tmate enabled"). + BoolVar(&c.Tmate.Enabled) + // shared pipeline flags c.Flags = internal.ParseFlags(cmd) } diff --git a/command/daemon/config.go b/command/daemon/config.go index 6d4e0a7..ebfcd8e 100644 --- a/command/daemon/config.go +++ b/command/daemon/config.go @@ -106,6 +106,11 @@ type Config struct { Config string `envconfig:"DRONE_DOCKER_CONFIG"` Stream bool `envconfig:"DRONE_DOCKER_STREAM_PULL" default:"true"` } + + Tmate struct { + Enabled bool `envconfig:"DRONE_TMATE_ENABLED" default:"true"` + Image string `envconfig:"DRONE_TMATE_IMAGE" default:"drone/drone-runner-docker:latest"` + } } // legacy environment variables. the key is the legacy diff --git a/command/daemon/daemon.go b/command/daemon/daemon.go index a6a343d..2570228 100644 --- a/command/daemon/daemon.go +++ b/command/daemon/daemon.go @@ -141,6 +141,10 @@ func (c *daemonCommand) run(*kingpin.ParseContext) error { CPUSet: config.Resources.CPUSet, ShmSize: config.Resources.ShmSize, }, + Tmate: compiler.Tmate{ + Image: config.Tmate.Image, + Enabled: config.Tmate.Enabled, + }, Environ: provider.Combine( provider.Static(config.Runner.Environ), provider.External( diff --git a/command/exec.go b/command/exec.go index f448062..e96af45 100644 --- a/command/exec.go +++ b/command/exec.go @@ -49,6 +49,7 @@ type execCommand struct { Labels map[string]string Secrets map[string]string Resources compiler.Resources + Tmate compiler.Tmate Clone bool Config string Pretty bool @@ -120,6 +121,7 @@ func (c *execCommand) run(*kingpin.ParseContext) error { Environ: provider.Static(c.Environ), Labels: c.Labels, Resources: c.Resources, + Tmate: c.Tmate, Privileged: append(c.Privileged, compiler.Privileged...), Networks: c.Networks, Volumes: c.Volumes, @@ -327,6 +329,13 @@ func registerExec(app *kingpin.Application) { cmd.Flag("docker-config", "path to the docker config file"). StringVar(&c.Config) + cmd.Flag("tmate-image", "tmate docker image"). + Default("drone/drone-runner-docker:latest"). + StringVar(&c.Tmate.Image) + + cmd.Flag("tmate-enabled", "tmate enabled"). + BoolVar(&c.Tmate.Enabled) + cmd.Flag("debug", "enable debug logging"). BoolVar(&c.Debug) diff --git a/docker/Dockerfile.linux.amd64 b/docker/Dockerfile.linux.amd64 index c662eba..11571ec 100644 --- a/docker/Dockerfile.linux.amd64 +++ b/docker/Dockerfile.linux.amd64 @@ -1,7 +1,12 @@ -FROM alpine:3.6 as alpine +FROM alpine:3 as alpine RUN apk add -U --no-cache ca-certificates -FROM alpine:3.6 +RUN wget https://github.com/tmate-io/tmate/releases/download/2.4.0/tmate-2.4.0-static-linux-amd64.tar.xz +RUN tar -xf tmate-2.4.0-static-linux-amd64.tar.xz +RUN mv tmate-2.4.0-static-linux-amd64/tmate /bin/ +RUN chmod +x /bin/tmate + +FROM alpine:3 EXPOSE 3000 ENV GODEBUG netdns=go @@ -9,6 +14,7 @@ ENV DRONE_PLATFORM_OS linux ENV DRONE_PLATFORM_ARCH amd64 COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=alpine /bin/tmate /bin LABEL com.centurylinklabs.watchtower.stop-signal="SIGINT" diff --git a/docker/Dockerfile.linux.arm b/docker/Dockerfile.linux.arm index e46adf5..c1c7335 100644 --- a/docker/Dockerfile.linux.arm +++ b/docker/Dockerfile.linux.arm @@ -1,7 +1,12 @@ -FROM alpine:3.6 as alpine +FROM alpine:3 as alpine RUN apk add -U --no-cache ca-certificates -FROM alpine:3.6 +RUN wget https://github.com/tmate-io/tmate/releases/download/2.4.0/tmate-2.4.0-static-linux-arm32v7.tar.xz +RUN tar -xf tmate-2.4.0-static-linux-arm32v7.tar.xz +RUN mv tmate-2.4.0-static-linux-arm32v7/tmate /bin/ +RUN chmod +x /bin/tmate + +FROM alpine:3 EXPOSE 3000 ENV GODEBUG netdns=go diff --git a/docker/Dockerfile.linux.arm64 b/docker/Dockerfile.linux.arm64 index 450e383..a660d8c 100644 --- a/docker/Dockerfile.linux.arm64 +++ b/docker/Dockerfile.linux.arm64 @@ -1,7 +1,12 @@ -FROM alpine:3.6 as alpine +FROM alpine:3 as alpine RUN apk add -U --no-cache ca-certificates -FROM alpine:3.6 +RUN wget https://github.com/tmate-io/tmate/releases/download/2.4.0/tmate-2.4.0-static-linux-arm64v8.tar.xz +RUN tar -xf tmate-2.4.0-static-linux-arm64v8.tar.xz +RUN mv tmate-2.4.0-static-linux-arm64v8/tmate /bin/ +RUN chmod +x /bin/tmate + +FROM alpine:3 EXPOSE 3000 ENV GODEBUG netdns=go diff --git a/engine/compiler/compiler.go b/engine/compiler/compiler.go index 3b53f00..3a4245e 100644 --- a/engine/compiler/compiler.go +++ b/engine/compiler/compiler.go @@ -53,6 +53,12 @@ type Resources struct { ShmSize int64 } +// Tmate defines tmate settings. +type Tmate struct { + Image string + Enabled bool +} + // Compiler compiles the Yaml configuration file to an // intermediate representation optimized for simple execution. type Compiler struct { @@ -92,6 +98,10 @@ type Compiler struct { // applies to pipeline containers. Resources Resources + // Tate provides global configration options for tmate + // live debugging. + Tmate Tmate + // Secret returns a named secret value that can be injected // into the pipeline step. Secret secret.Provider @@ -307,6 +317,38 @@ func (c *Compiler) Compile(ctx context.Context, args runtime.CompilerArgs) runti } } + // create internal steps if build running in debug mode + if c.Tmate.Enabled && args.Build.Debug && pipeline.Platform.OS != "windows" { + // first we need to add an internal setup step to the pipeline + // to copy over the tmate binary. Internal steps are not visible + // to the end user. + spec.Internal = append(spec.Internal, &engine.Step{ + ID: random(), + Labels: labels, + Pull: engine.PullIfNotExists, + Image: image.Expand(c.Tmate.Image), + Entrypoint: []string{"/bin/sh", "-c"}, + Command: []string{"cp /bin/tmate /usr/drone/bin/"}, + Network: "none", + }) + + // next we create a temporary volume to share the tmate binary + // with the pipeline containers. + for _, step := range append(spec.Steps, spec.Internal...) { + step.Volumes = append(step.Volumes, &engine.VolumeMount{ + Name: "_addons", + Path: "/usr/drone/bin", + }) + } + spec.Volumes = append(spec.Volumes, &engine.Volume{ + EmptyDir: &engine.VolumeEmptyDir{ + ID: random(), + Name: "_addons", + Labels: labels, + }, + }) + } + if isGraph(spec) == false { configureSerial(spec) } else if pipeline.Clone.Disable == false { @@ -345,7 +387,7 @@ func (c *Compiler) Compile(ctx context.Context, args runtime.CompilerArgs) runti } } - for _, step := range spec.Steps { + for _, step := range append(spec.Steps, spec.Internal...) { STEPS: for _, cred := range creds { if image.MatchHostname(step.Image, cred.Address) { diff --git a/engine/compiler/shell/shell.go b/engine/compiler/shell/shell.go index dd72058..c37a600 100644 --- a/engine/compiler/shell/shell.go +++ b/engine/compiler/shell/shell.go @@ -58,23 +58,8 @@ echo + %s const tmateScript = ` remote_debug() { - if [ "$?" -ne "0" ]; - then - - if command -v apt-get &> /dev/null - then - apt-get update -qq - apt-get install xz-utils --assume-yes -qq - fi - - wget https://github.com/tmate-io/tmate/releases/download/2.4.0/tmate-2.4.0-static-linux-amd64.tar.xz - tar -xf tmate-2.4.0-static-linux-amd64.tar.xz - mv tmate-2.4.0-static-linux-amd64/tmate /usr/bin/ - chmod +x /usr/bin/tmate - rm -rf tmate-2.4.0-static-linux-amd64 - rm -rf tmate-2.4.0-static-linux-amd64.tar.xz - tmate -F - + if [ "$?" -ne "0" ]; then + /usr/drone/bin/tmate -F fi } diff --git a/engine/engine.go b/engine/engine.go index c7faebb..2a559c1 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -8,6 +8,7 @@ import ( "context" "io" "io/ioutil" + "time" "github.com/drone-runners/drone-runner-docker/internal/docker/errors" "github.com/drone-runners/drone-runner-docker/internal/docker/image" @@ -90,6 +91,35 @@ func (e *Docker) Setup(ctx context.Context, specv runtime.Spec) error { Labels: spec.Network.Labels, }) + // launches the inernal setup steps + for _, step := range spec.Internal { + if err := e.create(ctx, spec, step, ioutil.Discard); err != nil { + logger.FromContext(ctx). + WithError(err). + WithField("container", step.ID). + Errorln("cannot create tmate container") + return err + } + if err := e.start(ctx, step.ID); err != nil { + logger.FromContext(ctx). + WithError(err). + WithField("container", step.ID). + Errorln("cannot start tmate container") + return err + } + if !step.Detach { + // the internal containers perform short-lived tasks + // and should not require > 1 minute to execute. + // + // just to be on the safe side we apply a timeout to + // ensure we never block pipeline execution because we + // are waiting on an internal task. + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + e.wait(ctx, step.ID) + } + } + return errors.TrimExtraInfo(err) } @@ -104,12 +134,12 @@ func (e *Docker) Destroy(ctx context.Context, specv runtime.Spec) error { } // stop all containers - for _, step := range spec.Steps { + for _, step := range append(spec.Steps, spec.Internal...) { e.client.ContainerKill(ctx, step.ID, "9") } // cleanup all containers - for _, step := range spec.Steps { + for _, step := range append(spec.Steps, spec.Internal...) { e.client.ContainerRemove(ctx, step.ID, removeOpts) } diff --git a/engine/spec.go b/engine/spec.go index 9ad219a..5467a86 100644 --- a/engine/spec.go +++ b/engine/spec.go @@ -17,6 +17,7 @@ type ( Spec struct { Platform Platform `json:"platform,omitempty"` Steps []*Step `json:"steps,omitempty"` + Internal []*Step `json:"internal,omitempty"` Volumes []*Volume `json:"volumes,omitempty"` Network Network `json:"network"` }