diff --git a/pkg/skaffold/debug/container.go b/pkg/skaffold/debug/container.go index ead14d385d0..c53a3359058 100644 --- a/pkg/skaffold/debug/container.go +++ b/pkg/skaffold/debug/container.go @@ -53,14 +53,14 @@ type containerTransformer interface { // and required initContainer (an empty string if not required), or return a non-nil error if // the container could not be transformed. The initContainer image is intended to install any // required debug support tools. - Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string) (types.ContainerDebugConfiguration, string, error) + Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) } // TransformContainer rewrites the container definition to enable debugging. // Returns a debugging configuration description with associated language runtime support // container image, or an error if the rewrite was unsuccessful. -func TransformContainer(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator) (types.ContainerDebugConfiguration, string, error) { - configuration, requiredImage, err := transformContainer(adapter, config, portAlloc) +func TransformContainer(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { + configuration, requiredImage, err := transformContainer(adapter, config, portAlloc, dmd) if err == nil { configuration.Artifact = config.Artifact if configuration.WorkingDir == "" { @@ -70,7 +70,7 @@ func TransformContainer(adapter types.ContainerAdapter, config ImageConfiguratio return configuration, requiredImage, err } -func transformContainer(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator) (types.ContainerDebugConfiguration, string, error) { +func transformContainer(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { // Update the image configuration's environment with those set in the k8s manifest. // (Environment variables in the k8s container's `env` add to the image configuration's `env` settings rather than replace.) container := adapter.GetContainer() @@ -91,7 +91,7 @@ func transformContainer(adapter types.ContainerAdapter, config ImageConfiguratio // Apply command-line unwrapping for buildpack images and images using `sh -c`-style command-lines next := func(adapter types.ContainerAdapter, config ImageConfiguration) (types.ContainerDebugConfiguration, string, error) { - return performContainerTransform(adapter, config, portAlloc) + return performContainerTransform(adapter, config, portAlloc, dmd) } if isCNBImage(config) { return updateForCNBImage(adapter, config, next) @@ -151,20 +151,20 @@ func isShDashC(cmd, arg string) bool { return (cmd == "/bin/sh" || cmd == "/bin/bash") && arg == "-c" } -func performContainerTransform(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator) (types.ContainerDebugConfiguration, string, error) { +func performContainerTransform(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { log.Entry(context.TODO()).Tracef("Examining container %q with config %v", adapter.GetContainer().Name, config) // This approach prioritizes the user-defined runtime specified in the configuration. If the user explicitly defines the runtime, we assume they have a // specific intention and want to use that specific transform. If no explicit runtime is specified, the code tries to infer the appropriate transform // based on other indicators in the configuration. for transform := range containerTransforms { if transform.MatchRuntime(config) { - return transform.Apply(adapter, config, portAlloc, Protocols) + return transform.Apply(adapter, config, portAlloc, Protocols, dmd) } } for transform := range containerTransforms { if transform.IsApplicable(config) { - return transform.Apply(adapter, config, portAlloc, Protocols) + return transform.Apply(adapter, config, portAlloc, Protocols, dmd) } } return types.ContainerDebugConfiguration{}, "", fmt.Errorf("unable to determine runtime for %q", adapter.GetContainer().Name) diff --git a/pkg/skaffold/debug/transform.go b/pkg/skaffold/debug/transform.go index 3cf09cc8d7a..3afd09a0191 100644 --- a/pkg/skaffold/debug/transform.go +++ b/pkg/skaffold/debug/transform.go @@ -67,6 +67,14 @@ type PortAllocator func(int32) int32 // configurationRetriever retrieves an container image configuration type ConfigurationRetriever func(string) (ImageConfiguration, error) +// DebuggerMetaData captures info for debug tranformers to construct the cli command appropriately +type DebuggerMetaData struct { + // ShouldWait hints that the process should start and wait for the debugger to be attached. + // For kubernetes this derives from an annotation at Pod-Level skaffold.dev/debug-suspend and + // for docker from an image label skaffold.dev/debug-suspend. + ShouldWait bool +} + // ImageConfiguration captures information from a docker/oci image configuration. // It also includes a "artifact", usually containing the corresponding artifact's' image name from `skaffold.yaml`. type ImageConfiguration struct { @@ -92,6 +100,20 @@ var entrypointLaunchers []string var Protocols = []string{} +// ExtractDebuggerMetaData extracts and returns the appropriate DebuggerMetaData based on the passed map[string]string +// which contains the context-appropriate metadata, e.g. for Kubernetes deployment the annotations and for Docker the image +// Labels. +func ExtractDebuggerMetaData(md map[string]string) *DebuggerMetaData { + dmd := &DebuggerMetaData{} + + if md == nil { + return dmd + } + + _, dmd.ShouldWait = md["skaffold.dev/debug-suspend"] + return dmd +} + // isEntrypointLauncher checks if the given entrypoint is a known entrypoint launcher, // meaning an entrypoint that treats the image's CMD as a command-line. func isEntrypointLauncher(entrypoint []string) bool { diff --git a/pkg/skaffold/debug/transform_go.go b/pkg/skaffold/debug/transform_go.go index a5f370bc06e..e88bf23e86b 100644 --- a/pkg/skaffold/debug/transform_go.go +++ b/pkg/skaffold/debug/transform_go.go @@ -51,11 +51,12 @@ type dlvSpec struct { port uint16 headless bool log bool + wait bool apiVersion int } func newDlvSpec(port uint16) dlvSpec { - return dlvSpec{mode: "exec", port: port, apiVersion: defaultAPIVersion, headless: true} + return dlvSpec{mode: "exec", port: port, apiVersion: defaultAPIVersion, headless: true, wait: true} } // isLaunchingDlv determines if the arguments seems to be invoking Delve @@ -110,7 +111,7 @@ func (t dlvTransformer) IsApplicable(config ImageConfiguration) bool { // Apply configures a container definition for Go with Delve. // Returns the debug configuration details, with the "go" support image -func (t dlvTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string) (types.ContainerDebugConfiguration, string, error) { +func (t dlvTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { container := adapter.GetContainer() log.Entry(context.TODO()).Infof("Configuring %q for Go/Delve debugging", container.Name) @@ -120,6 +121,13 @@ func (t dlvTransformer) Apply(adapter types.ContainerAdapter, config ImageConfig if spec == nil { newSpec := newDlvSpec(uint16(portAlloc(defaultDlvPort))) spec = &newSpec + + if dmd != nil { + spec.wait = dmd.ShouldWait + } else { + spec.wait = false + } + switch { case len(config.Entrypoint) > 0 && !isEntrypointLauncher(config.Entrypoint): container.Command = rewriteDlvCommandLine(config.Entrypoint, *spec, container.Args) @@ -155,7 +163,7 @@ func extractDlvSpec(args []string) *dlvSpec { return nil } // delve's defaults - spec := dlvSpec{apiVersion: 2, log: false, headless: false} + spec := dlvSpec{apiVersion: 2, log: false, headless: false, wait: true} arguments: for _, arg := range args { switch { @@ -167,6 +175,8 @@ arguments: spec.headless = true case arg == "--log": spec.log = true + case arg == "--continue": + spec.wait = false case strings.HasPrefix(arg, "--listen="): address := strings.SplitN(arg, "=", 2)[1] split := strings.SplitN(address, ":", 2) @@ -207,7 +217,12 @@ func (spec dlvSpec) asArguments() []string { if spec.headless { args = append(args, "--headless") } - args = append(args, "--continue", "--accept-multiclient") + + if !spec.wait { + args = append(args, "--continue") + } + + args = append(args, "--accept-multiclient") if spec.port > 0 { args = append(args, fmt.Sprintf("--listen=%s:%d", spec.host, spec.port)) } else { diff --git a/pkg/skaffold/debug/transform_go_test.go b/pkg/skaffold/debug/transform_go_test.go index 6497b543c6a..645d8619bc2 100644 --- a/pkg/skaffold/debug/transform_go_test.go +++ b/pkg/skaffold/debug/transform_go_test.go @@ -28,7 +28,7 @@ import ( func TestNewDlvSpecDefaults(t *testing.T) { spec := newDlvSpec(20) - expected := dlvSpec{mode: "exec", port: 20, apiVersion: 2, headless: true, log: false} + expected := dlvSpec{mode: "exec", port: 20, apiVersion: 2, headless: true, log: false, wait: true} testutil.CheckDeepEqual(t, expected, spec, cmp.AllowUnexported(spec)) } @@ -40,14 +40,15 @@ func TestExtractDlvArg(t *testing.T) { {nil, nil}, {[]string{"foo"}, nil}, {[]string{"foo", "--foo"}, nil}, - {[]string{"dlv", "debug", "--headless"}, &dlvSpec{mode: "debug", headless: true, apiVersion: 2, log: false}}, - {[]string{"dlv", "--headless", "exec"}, &dlvSpec{mode: "exec", headless: true, apiVersion: 2, log: false}}, - {[]string{"dlv", "--headless", "exec", "--", "--listen=host:4345"}, &dlvSpec{mode: "exec", headless: true, apiVersion: 2, log: false}}, - {[]string{"dlv", "test", "--headless", "--listen=host:4345"}, &dlvSpec{mode: "test", host: "host", port: 4345, headless: true, apiVersion: 2, log: false}}, - {[]string{"dlv", "debug", "--headless", "--api-version=1"}, &dlvSpec{mode: "debug", headless: true, apiVersion: 1, log: false}}, - {[]string{"dlv", "debug", "--listen=host:4345", "--headless", "--api-version=2", "--log"}, &dlvSpec{mode: "debug", host: "host", port: 4345, headless: true, apiVersion: 2, log: true}}, - {[]string{"dlv", "debug", "--listen=:4345"}, &dlvSpec{mode: "debug", port: 4345, apiVersion: 2}}, - {[]string{"dlv", "debug", "--listen=host:"}, &dlvSpec{mode: "debug", host: "host", apiVersion: 2}}, + {[]string{"dlv", "debug", "--headless"}, &dlvSpec{mode: "debug", headless: true, apiVersion: 2, log: false, wait: true}}, + {[]string{"dlv", "--headless", "exec"}, &dlvSpec{mode: "exec", headless: true, apiVersion: 2, log: false, wait: true}}, + {[]string{"dlv", "--headless", "exec", "--", "--listen=host:4345"}, &dlvSpec{mode: "exec", headless: true, apiVersion: 2, log: false, wait: true}}, + {[]string{"dlv", "test", "--headless", "--listen=host:4345"}, &dlvSpec{mode: "test", host: "host", port: 4345, headless: true, apiVersion: 2, log: false, wait: true}}, + {[]string{"dlv", "debug", "--headless", "--api-version=1"}, &dlvSpec{mode: "debug", headless: true, apiVersion: 1, log: false, wait: true}}, + {[]string{"dlv", "debug", "--listen=host:4345", "--headless", "--api-version=2", "--log"}, &dlvSpec{mode: "debug", host: "host", port: 4345, headless: true, apiVersion: 2, log: true, wait: true}}, + {[]string{"dlv", "debug", "--listen=:4345"}, &dlvSpec{mode: "debug", port: 4345, apiVersion: 2, wait: true}}, + {[]string{"dlv", "debug", "--listen=host:"}, &dlvSpec{mode: "debug", host: "host", apiVersion: 2, wait: true}}, + {[]string{"dlv", "debug", "--listen=host:", "--continue"}, &dlvSpec{mode: "debug", host: "host", apiVersion: 2, wait: false}}, } for _, test := range tests { testutil.Run(t, strings.Join(test.in, " "), func(t *testutil.T) { diff --git a/pkg/skaffold/debug/transform_jvm.go b/pkg/skaffold/debug/transform_jvm.go index 6e7bd342952..473f6a2da2a 100644 --- a/pkg/skaffold/debug/transform_jvm.go +++ b/pkg/skaffold/debug/transform_jvm.go @@ -77,7 +77,7 @@ type jdwpSpec struct { // Apply configures a container definition for JVM debugging. // Returns a simple map describing the debug configuration details. -func (t jdwpTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string) (types.ContainerDebugConfiguration, string, error) { +func (t jdwpTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { container := adapter.GetContainer() log.Entry(context.TODO()).Infof("Configuring %q for JVM debugging", container.Name) // try to find existing JAVA_TOOL_OPTIONS or jdwp command argument @@ -88,7 +88,15 @@ func (t jdwpTransformer) Apply(adapter types.ContainerAdapter, config ImageConfi port = int32(spec.port) } else { port = portAlloc(defaultJdwpPort) - jto := fmt.Sprintf("-agentlib:jdwp=transport=dt_socket,server=y,address=%d,suspend=n,quiet=y", port) + + var suspendParam string + if dmd != nil && dmd.ShouldWait { + suspendParam = "y" + } else { + suspendParam = "n" + } + + jto := fmt.Sprintf("-agentlib:jdwp=transport=dt_socket,server=y,address=%d,suspend=%s,quiet=y", port, suspendParam) if existing, found := config.Env["JAVA_TOOL_OPTIONS"]; found { jto = existing + " " + jto } diff --git a/pkg/skaffold/debug/transform_netcore.go b/pkg/skaffold/debug/transform_netcore.go index 302f82f1ba9..488d20cb637 100644 --- a/pkg/skaffold/debug/transform_netcore.go +++ b/pkg/skaffold/debug/transform_netcore.go @@ -80,7 +80,7 @@ func (t netcoreTransformer) IsApplicable(config ImageConfiguration) bool { // Apply configures a container definition for vsdbg. // Returns a simple map describing the debug configuration details. -func (t netcoreTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string) (types.ContainerDebugConfiguration, string, error) { +func (t netcoreTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { container := adapter.GetContainer() log.Entry(context.TODO()).Infof("Configuring %q for netcore debugging", container.Name) diff --git a/pkg/skaffold/debug/transform_nodejs.go b/pkg/skaffold/debug/transform_nodejs.go index 222278c9c11..97f5fb19d8f 100644 --- a/pkg/skaffold/debug/transform_nodejs.go +++ b/pkg/skaffold/debug/transform_nodejs.go @@ -87,7 +87,7 @@ func (t nodeTransformer) IsApplicable(config ImageConfiguration) bool { // Apply configures a container definition for NodeJS Chrome V8 Inspector. // Returns a simple map describing the debug configuration details. -func (t nodeTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string) (types.ContainerDebugConfiguration, string, error) { +func (t nodeTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { container := adapter.GetContainer() log.Entry(context.TODO()).Infof("Configuring %q for node.js debugging", container.Name) @@ -95,6 +95,12 @@ func (t nodeTransformer) Apply(adapter types.ContainerAdapter, config ImageConfi spec := retrieveNodeInspectSpec(config) if spec == nil { spec = &inspectSpec{host: "0.0.0.0", port: portAlloc(defaultDevtoolsPort)} + if dmd != nil { + spec.brk = dmd.ShouldWait + } else { + spec.brk = false + } + switch { case isLaunchingNode(config.Entrypoint): container.Command = rewriteNodeCommandLine(config.Entrypoint, *spec) diff --git a/pkg/skaffold/debug/transform_python.go b/pkg/skaffold/debug/transform_python.go index f3148efac12..68a7e040e6d 100644 --- a/pkg/skaffold/debug/transform_python.go +++ b/pkg/skaffold/debug/transform_python.go @@ -118,7 +118,7 @@ func (t pythonTransformer) IsApplicable(config ImageConfiguration) bool { // Apply configures a container definition for Python with ptvsd/debugpy/pydevd. // Returns a simple map describing the debug configuration details. -func (t pythonTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string) (types.ContainerDebugConfiguration, string, error) { +func (t pythonTransformer) Apply(adapter types.ContainerAdapter, config ImageConfiguration, portAlloc PortAllocator, overrideProtocols []string, dmd *DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { container := adapter.GetContainer() log.Entry(context.TODO()).Infof("Configuring %q for python debugging", container.Name) @@ -132,7 +132,12 @@ func (t pythonTransformer) Apply(adapter types.ContainerAdapter, config ImageCon }, "", nil } - spec := createPythonDebugSpec(overrideProtocols, portAlloc) + shouldWait := false + if dmd != nil { + shouldWait = dmd.ShouldWait + } + + spec := createPythonDebugSpec(overrideProtocols, portAlloc, shouldWait) switch { case isLaunchingPython(config.Entrypoint): @@ -177,17 +182,17 @@ func extractPythonDebugSpec(args []string) *pythonSpec { return nil } -func createPythonDebugSpec(overrideProtocols []string, portAlloc PortAllocator) *pythonSpec { +func createPythonDebugSpec(overrideProtocols []string, portAlloc PortAllocator, shouldWait bool) *pythonSpec { for _, p := range overrideProtocols { switch p { case pydevdProtocol: - return &pythonSpec{debugger: pydevd, port: portAlloc(defaultPydevdPort)} + return &pythonSpec{debugger: pydevd, port: portAlloc(defaultPydevdPort), wait: shouldWait} case dapProtocol: - return &pythonSpec{debugger: debugpy, port: portAlloc(defaultDebugpyPort)} + return &pythonSpec{debugger: debugpy, port: portAlloc(defaultDebugpyPort), wait: shouldWait} } } - return &pythonSpec{debugger: debugpy, port: portAlloc(defaultDebugpyPort)} + return &pythonSpec{debugger: debugpy, port: portAlloc(defaultDebugpyPort), wait: shouldWait} } func extractPtvsdSpec(args []string) *pythonSpec { diff --git a/pkg/skaffold/docker/debugger/transform.go b/pkg/skaffold/docker/debugger/transform.go index 7fea67cecee..8fcbf421e6a 100644 --- a/pkg/skaffold/docker/debugger/transform.go +++ b/pkg/skaffold/docker/debugger/transform.go @@ -58,7 +58,9 @@ func transformImage(ctx context.Context, artifact graph.Artifact, cfg *container // the set of image IDs required to provide debugging support files requiredSupportImages := make(map[string]bool) - if configuration, requiredImage, err := debug.TransformContainer(adapter, imageConfig, portAlloc); err == nil { + dmd := debug.ExtractDebuggerMetaData(imageConfig.Labels) + + if configuration, requiredImage, err := debug.TransformContainer(adapter, imageConfig, portAlloc, dmd); err == nil { configurations[adapter.GetContainer().Name] = configuration if len(requiredImage) > 0 { log.Entry(ctx).Infof("%q requires debugging support image %q", cfg.Image, requiredImage) diff --git a/pkg/skaffold/kubernetes/debugging/debug_test.go b/pkg/skaffold/kubernetes/debugging/debug_test.go index 96be65c50c1..1f436175a69 100644 --- a/pkg/skaffold/kubernetes/debugging/debug_test.go +++ b/pkg/skaffold/kubernetes/debugging/debug_test.go @@ -41,7 +41,7 @@ func (t testTransformer) MatchRuntime(config debug.ImageConfiguration) bool { return true } -func (t testTransformer) Apply(adapter types.ContainerAdapter, config debug.ImageConfiguration, portAlloc debug.PortAllocator, overrideProtocols []string) (types.ContainerDebugConfiguration, string, error) { +func (t testTransformer) Apply(adapter types.ContainerAdapter, config debug.ImageConfiguration, portAlloc debug.PortAllocator, overrideProtocols []string, dmd *debug.DebuggerMetaData) (types.ContainerDebugConfiguration, string, error) { port := portAlloc(9999) container := adapter.GetContainer() container.Ports = append(container.Ports, types.ContainerPort{Name: "test", ContainerPort: port}) diff --git a/pkg/skaffold/kubernetes/debugging/transform.go b/pkg/skaffold/kubernetes/debugging/transform.go index 86fad2b77d9..4ea91fb313f 100644 --- a/pkg/skaffold/kubernetes/debugging/transform.go +++ b/pkg/skaffold/kubernetes/debugging/transform.go @@ -267,7 +267,10 @@ func rewriteContainers(metadata *metav1.ObjectMeta, podSpec *v1.PodSpec, retriev a := adapter.NewAdapter(&container) // requiredImage, if not empty, is the image ID providing the debugging support files // `err != nil` means that the container did not or could not be transformed - if configuration, requiredImage, err := debug.TransformContainer(a, imageConfig, portAlloc); err == nil { + + dmd := debug.ExtractDebuggerMetaData(metadata.Annotations) + + if configuration, requiredImage, err := debug.TransformContainer(a, imageConfig, portAlloc, dmd); err == nil { configurations[container.Name] = configuration podSpec.Containers[i] = container // apply any configuration changes if len(requiredImage) > 0 { diff --git a/pkg/skaffold/kubernetes/debugging/transform_go_test.go b/pkg/skaffold/kubernetes/debugging/transform_go_test.go index f425311ef3b..e6c5c6db5a3 100644 --- a/pkg/skaffold/kubernetes/debugging/transform_go_test.go +++ b/pkg/skaffold/kubernetes/debugging/transform_go_test.go @@ -40,6 +40,7 @@ func TestDlvTransformerApply(t *testing.T) { result v1.Container debugConfig types.ContainerDebugConfiguration image string + dmd *debug.DebuggerMetaData }{ { description: "empty", @@ -58,6 +59,20 @@ func TestDlvTransformerApply(t *testing.T) { debugConfig: types.ContainerDebugConfiguration{Runtime: "go", Ports: map[string]uint32{"dlv": 56268}}, image: "go", }, + { + description: "basic with wait debugger", + containerSpec: v1.Container{}, + configuration: debug.ImageConfiguration{Entrypoint: []string{"app", "arg"}}, + result: v1.Container{ + Command: []string{"/dbg/go/bin/dlv", "exec", "--headless", "--accept-multiclient", "--listen=:56268", "--api-version=2", "app", "--", "arg"}, + Ports: []v1.ContainerPort{{Name: "dlv", ContainerPort: 56268}}, + }, + debugConfig: types.ContainerDebugConfiguration{Runtime: "go", Ports: map[string]uint32{"dlv": 56268}}, + image: "go", + dmd: &debug.DebuggerMetaData{ + ShouldWait: true, + }, + }, { description: "existing port", containerSpec: v1.Container{ @@ -116,7 +131,7 @@ func TestDlvTransformerApply(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { adapter := adapter.NewAdapter(&test.containerSpec) - config, image, err := debug.NewDlvTransformer().Apply(adapter, test.configuration, identity, nil) + config, image, err := debug.NewDlvTransformer().Apply(adapter, test.configuration, identity, nil, test.dmd) adapter.Apply() t.CheckError(test.shouldErr, err) diff --git a/pkg/skaffold/kubernetes/debugging/transform_jvm_test.go b/pkg/skaffold/kubernetes/debugging/transform_jvm_test.go index 8ade1df3fa2..d971d15ca3e 100644 --- a/pkg/skaffold/kubernetes/debugging/transform_jvm_test.go +++ b/pkg/skaffold/kubernetes/debugging/transform_jvm_test.go @@ -39,6 +39,7 @@ func TestJdwpTransformerApply(t *testing.T) { result v1.Container debugConfig types.ContainerDebugConfiguration image string + dmd *debug.DebuggerMetaData }{ { description: "empty", @@ -62,6 +63,21 @@ func TestJdwpTransformerApply(t *testing.T) { }, debugConfig: types.ContainerDebugConfiguration{Runtime: "jvm", Ports: map[string]uint32{"jdwp": 5005}}, }, + { + description: "existing port with suspend debugger", + containerSpec: v1.Container{ + Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}}, + }, + configuration: debug.ImageConfiguration{}, + result: v1.Container{ + Env: []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=y,quiet=y"}}, + Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}, {Name: "jdwp", ContainerPort: 5005}}, + }, + debugConfig: types.ContainerDebugConfiguration{Runtime: "jvm", Ports: map[string]uint32{"jdwp": 5005}}, + dmd: &debug.DebuggerMetaData{ + ShouldWait: true, + }, + }, { description: "existing jdwp spec", containerSpec: v1.Container{ @@ -95,7 +111,7 @@ func TestJdwpTransformerApply(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { adapter := adapter.NewAdapter(&test.containerSpec) - config, image, err := debug.NewJDWPTransformer().Apply(adapter, test.configuration, identity, nil) + config, image, err := debug.NewJDWPTransformer().Apply(adapter, test.configuration, identity, nil, test.dmd) adapter.Apply() // Apply never fails since there's always the option to set JAVA_TOOL_OPTIONS diff --git a/pkg/skaffold/kubernetes/debugging/transform_netcore_test.go b/pkg/skaffold/kubernetes/debugging/transform_netcore_test.go index f7c96ae49b0..b295db55b69 100644 --- a/pkg/skaffold/kubernetes/debugging/transform_netcore_test.go +++ b/pkg/skaffold/kubernetes/debugging/transform_netcore_test.go @@ -63,7 +63,7 @@ func TestNetcoreTransformerApply(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { adapter := adapter.NewAdapter(&test.containerSpec) - config, image, err := debug.NewNetcoreTransformer().Apply(adapter, test.configuration, identity, nil) + config, image, err := debug.NewNetcoreTransformer().Apply(adapter, test.configuration, identity, nil, nil) adapter.Apply() t.CheckError(test.shouldErr, err) diff --git a/pkg/skaffold/kubernetes/debugging/transform_nodejs_test.go b/pkg/skaffold/kubernetes/debugging/transform_nodejs_test.go index 9070cf11bf2..99c45565040 100644 --- a/pkg/skaffold/kubernetes/debugging/transform_nodejs_test.go +++ b/pkg/skaffold/kubernetes/debugging/transform_nodejs_test.go @@ -39,6 +39,7 @@ func TestNodeTransformer_Apply(t *testing.T) { configuration debug.ImageConfiguration result v1.Container debugConfig types.ContainerDebugConfiguration + dmd *debug.DebuggerMetaData }{ { description: "empty", @@ -72,6 +73,20 @@ func TestNodeTransformer_Apply(t *testing.T) { }, debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, }, + { + description: "entrypoint with PATH and brk", + containerSpec: v1.Container{}, + configuration: debug.ImageConfiguration{Entrypoint: []string{"node"}, Env: map[string]string{"PATH": "/usr/bin"}}, + result: v1.Container{ + Command: []string{"node", "--inspect-brk=0.0.0.0:9229"}, + Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin:/usr/bin"}}, + Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, + }, + debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, + dmd: &debug.DebuggerMetaData{ + ShouldWait: true, + }, + }, { description: "existing port", containerSpec: v1.Container{ @@ -140,7 +155,7 @@ func TestNodeTransformer_Apply(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { adapter := adapter.NewAdapter(&test.containerSpec) - config, image, err := debug.NewNodeTransformer().Apply(adapter, test.configuration, identity, nil) + config, image, err := debug.NewNodeTransformer().Apply(adapter, test.configuration, identity, nil, test.dmd) adapter.Apply() // Apply never fails since there's always the option to set NODE_OPTIONS diff --git a/pkg/skaffold/kubernetes/debugging/transform_python_test.go b/pkg/skaffold/kubernetes/debugging/transform_python_test.go index d1345230c8c..9c66163e1b2 100644 --- a/pkg/skaffold/kubernetes/debugging/transform_python_test.go +++ b/pkg/skaffold/kubernetes/debugging/transform_python_test.go @@ -41,6 +41,7 @@ func TestPythonTransformer_Apply(t *testing.T) { result v1.Container debugConfig types.ContainerDebugConfiguration image string + dmd *debug.DebuggerMetaData }{ { description: "empty", @@ -60,6 +61,20 @@ func TestPythonTransformer_Apply(t *testing.T) { debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}}, image: "python", }, + { + description: "basic with wait debugger", + containerSpec: v1.Container{}, + configuration: debug.ImageConfiguration{Entrypoint: []string{"python"}}, + result: v1.Container{ + Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--wait", "--", "python"}, + Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, + }, + debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}}, + image: "python", + dmd: &debug.DebuggerMetaData{ + ShouldWait: true, + }, + }, { description: "override protocol - pydevd, dap", containerSpec: v1.Container{}, @@ -137,7 +152,7 @@ func TestPythonTransformer_Apply(t *testing.T) { for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) { adapter := adapter.NewAdapter(&test.containerSpec) - config, image, err := debug.NewPythonTransformer().Apply(adapter, test.configuration, identity, test.overrideProtocols) + config, image, err := debug.NewPythonTransformer().Apply(adapter, test.configuration, identity, test.overrideProtocols, test.dmd) adapter.Apply() t.CheckError(test.shouldErr, err)