Skip to content

Commit

Permalink
feat: enabling running cdebug with images built locally (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
mauriciopoppe authored Feb 18, 2024
1 parent ae1bf59 commit f0f8b4d
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 10 deletions.
60 changes: 50 additions & 10 deletions cmd/exec/exec_docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,39 @@ import (
"github.com/iximiuz/cdebug/pkg/uuid"
)

// debugImageExistsLocally checks if the debug image exists in the host and that it matches the target container
// platform.
func debugImageExistsLocally(ctx context.Context, client *docker.Client, debugImage string, debugImagePlatform string, target types.ContainerJSON) (bool, error) {
debugImageInspect, _, err := client.ImageInspectWithRaw(ctx, debugImage)
if err != nil {
// err means that the requested image wasn't found,
// therefore it needs to be pulled
logrus.Debugf("The image %s wasn't found locally", debugImage)
return false, nil
}

debugImageOs := debugImageInspect.Os
debugImageArchitecture := debugImageInspect.Architecture
// override through --platform flag
if debugImagePlatform != "" {
fmt.Sscanf(debugImagePlatform, "%s/%s", &debugImageOs, &debugImageArchitecture)
}

// target.Platform only contains the Os but doesn't have the Arch, however we can
// get the Os & Arch by analyzing the image using target.Image
targetImageInspect, _, err := client.ImageInspectWithRaw(ctx, target.Image)
if err != nil {
return false, fmt.Errorf("failed to inspect image %s: %v", target.Image, err)
}

// debug image exists, also check that the platform matches the target image platform
if debugImageArchitecture != targetImageInspect.Architecture || debugImageOs != targetImageInspect.Os {
return false, nil
}

return true, nil
}

func runDebuggerDocker(ctx context.Context, cli cliutil.CLI, opts *options) error {
client, err := docker.NewClient(docker.Options{
Out: cli.AuxStream(),
Expand All @@ -33,16 +66,23 @@ func runDebuggerDocker(ctx context.Context, cli cliutil.CLI, opts *options) erro
return errTargetNotRunning
}

cli.PrintAux("Pulling debugger image...\n")
if err := client.ImagePullEx(ctx, opts.image, types.ImagePullOptions{
Platform: func() string {
if len(opts.platform) == 0 {
return target.Platform
}
return opts.platform
}(),
}); err != nil {
return errCannotPull(opts.image, err)
imageExists, err := debugImageExistsLocally(ctx, client, opts.image, opts.platform, target)
if err != nil {
return err
}

if !imageExists {
cli.PrintAux("Pulling debugger image...\n")
if err := client.ImagePullEx(ctx, opts.image, types.ImagePullOptions{
Platform: func() string {
if len(opts.platform) == 0 {
return target.Platform
}
return opts.platform
}(),
}); err != nil {
return errCannotPull(opts.image, err)
}
}

runID := uuid.ShortID()
Expand Down
23 changes: 23 additions & 0 deletions e2e/exec/docker_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package exec

import (
"fmt"
"regexp"
"testing"

"gotest.tools/assert"
Expand All @@ -21,6 +23,27 @@ func TestExecDockerSimple(t *testing.T) {
assert.Check(t, cmp.Contains(res.Stdout(), "debian"))
}

func TestExecDockerUseLocalImage(t *testing.T) {
localImage, cleanupLocalImage := fixture.DockerBuildLocalImage(t)
defer cleanupLocalImage()

targetImageID, targetImageCleanup := fixture.DockerRunBackground(t, fixture.ImageNginx, nil)
defer targetImageCleanup()

res := icmd.RunCmd(
icmd.Command("cdebug", "--image", localImage, "-l=debug", "exec", "--rm", "-q", targetImageID, "cat", "/etc/os-release"),
)
res.Assert(t, icmd.Success)
assert.Check(t, cmp.Contains(res.Stdout(), "debian"))
assert.Assert(t, func() cmp.Result {
re := regexp.MustCompile("Pulling debugger image...")
if re.MatchString(res.Stdout()) {
return cmp.ResultFailure(fmt.Sprintf("Image %s shouldn't be pulled because it only exists locally", localImage))
}
return cmp.ResultSuccess
})
}

func TestExecDockerHostNamespaces(t *testing.T) {
targetID, cleanup := fixture.DockerRunBackground(t, fixture.ImageNginx,
[]string{"--net", "host", "--pid", "host"},
Expand Down
1 change: 1 addition & 0 deletions e2e/internal/fixture/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FROM busybox:musl
26 changes: 26 additions & 0 deletions e2e/internal/fixture/fixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package fixture

import (
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -89,6 +91,30 @@ func DockerRunBackground(
return contID, cleanup
}

func DockerBuildLocalImage(
t *testing.T,
) (string, func()) {
localImage := "thisimageonlyexistslocally:1.0"

// Get dirname of current file, assumes that the Dockerfile lives next to this file.
_, filename, _, ok := runtime.Caller(0)
if !ok {
t.Fatalf("Failed to get filename")
}
dirname := filepath.Dir(filename)

cmd := dockerCmd("build", "-t", localImage, dirname)

res := icmd.RunCmd(cmd)
res.Assert(t, icmd.Success)

cleanup := func() {
icmd.RunCmd(dockerCmd("rmi", localImage)).Assert(t, icmd.Success)
}

return localImage, cleanup
}

func NerdctlRunBackground(
t *testing.T,
image string,
Expand Down

0 comments on commit f0f8b4d

Please sign in to comment.