github.com/GoogleContainerTools/skaffold/v2@v2.13.2/integration/debug_test.go (about)

     1  /*
     2  Copyright 2019 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package integration
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	dockertypes "github.com/docker/docker/api/types"
    27  
    28  	"github.com/GoogleContainerTools/skaffold/v2/integration/skaffold"
    29  	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/debug/types"
    30  	"github.com/GoogleContainerTools/skaffold/v2/proto/v1"
    31  	"github.com/GoogleContainerTools/skaffold/v2/testutil"
    32  )
    33  
    34  func TestDebug(t *testing.T) {
    35  	tests := []struct {
    36  		description   string
    37  		dir           string
    38  		config        string
    39  		args          []string
    40  		deployments   []string
    41  		pods          []string
    42  		ignoreWorkdir bool
    43  	}{
    44  		{
    45  			description: "kubectl",
    46  			dir:         "testdata/debug",
    47  			deployments: []string{"java"},
    48  			pods:        []string{"nodejs", "npm" /*, "python3"*/, "go" /*, "netcore"*/},
    49  		},
    50  		{
    51  			description: "kustomize",
    52  			dir:         "testdata/debug",
    53  			args:        []string{"--profile", "kustomize"},
    54  			deployments: []string{"java"},
    55  			pods:        []string{"nodejs", "npm" /*, "python3"*/, "go" /*, "netcore"*/},
    56  		},
    57  		{
    58  			description: "specified-runtime-nodejs",
    59  			dir:         "testdata/debug",
    60  			args:        []string{"--profile", "specified-runtime", "--check-cluster-node-platforms=false"},
    61  			pods:        []string{"nodejs"},
    62  		},
    63  		// TODO(#8811): Enable this test when issue is solve.
    64  		// {
    65  		// 	description: "buildpacks",
    66  		// 	dir:         "testdata/debug",
    67  		// 	args:        []string{"--profile", "buildpacks"},
    68  		// 	deployments: []string{"java"},
    69  		// 	pods:        []string{"nodejs", "npm" /*, "python3"*/, "go" /*, "netcore"*/},
    70  		// },
    71  		{
    72  			description:   "helm",
    73  			dir:           "examples/helm-deployment",
    74  			deployments:   []string{"skaffold-helm"},
    75  			ignoreWorkdir: true, // dockerfile doesn't have a workdir
    76  		},
    77  		{
    78  			description:   "modules",
    79  			dir:           "examples/multi-config-microservices",
    80  			deployments:   []string{"leeroy-web", "leeroy-app"},
    81  			ignoreWorkdir: true, // dockerfile doesn't have a workdir
    82  		},
    83  	}
    84  	for _, test := range tests {
    85  		t.Run(test.description, func(t *testing.T) {
    86  			MarkIntegrationTest(t, CanRunWithoutGcp)
    87  			// Run skaffold build first to fail quickly on a build failure
    88  			skaffold.Build(test.args...).InDir(test.dir).RunOrFail(t)
    89  
    90  			ns, client := SetupNamespace(t)
    91  
    92  			skaffold.Debug(test.args...).InDir(test.dir).InNs(ns.Name).RunBackground(t)
    93  
    94  			verifyDebugAnnotations := func(annotations map[string]string) {
    95  				var configs map[string]types.ContainerDebugConfiguration
    96  				if anno, found := annotations["debug.cloud.google.com/config"]; !found {
    97  					t.Errorf("deployment missing debug annotation: %v", annotations)
    98  				} else if err := json.Unmarshal([]byte(anno), &configs); err != nil {
    99  					t.Errorf("error unmarshalling debug annotation: %v: %v", anno, err)
   100  				} else {
   101  					for k, config := range configs {
   102  						if !test.ignoreWorkdir && config.WorkingDir == "" {
   103  							t.Errorf("debug config for %q missing WorkingDir: %v: %v", k, anno, config)
   104  						}
   105  						if config.Runtime == "" {
   106  							t.Errorf("debug config for %q missing Runtime: %v: %v", k, anno, config)
   107  						}
   108  					}
   109  				}
   110  			}
   111  
   112  			for _, podName := range test.pods {
   113  				pod := client.GetPod(podName)
   114  
   115  				annotations := pod.Annotations
   116  				verifyDebugAnnotations(annotations)
   117  			}
   118  
   119  			for _, depName := range test.deployments {
   120  				deploy := client.GetDeployment(depName)
   121  
   122  				annotations := deploy.Spec.Template.GetAnnotations()
   123  				verifyDebugAnnotations(annotations)
   124  			}
   125  		})
   126  	}
   127  }
   128  
   129  func TestDockerDebug(t *testing.T) {
   130  	t.Run("debug docker deployment", func(t *testing.T) {
   131  		MarkIntegrationTest(t, CanRunWithoutGcp)
   132  		skaffold.Build("-p", "docker").InDir("testdata/debug").RunOrFail(t)
   133  
   134  		skaffold.Debug("-p", "docker").InDir("testdata/debug").RunBackground(t)
   135  		defer skaffold.Delete("-p", "docker").InDir("testdata/debug").RunBackground(t)
   136  
   137  		// use docker client to verify container has been created properly
   138  		// check this container and verify entrypoint has been rewritten
   139  		client := SetupDockerClient(t)
   140  		var (
   141  			verifyEntrypointRewrite bool
   142  			verifySupportContainer  bool
   143  			tries                   = 0
   144  			sleepTime               = 2 * time.Second // retrieve containers every two seconds
   145  			maxTries                = 15              // try for 30 seconds max
   146  		)
   147  		for {
   148  			containers, err := client.ContainerList(context.Background(), dockertypes.ContainerListOptions{All: true})
   149  			if err != nil {
   150  				t.Fail()
   151  			}
   152  			time.Sleep(sleepTime)
   153  			if len(containers) == 0 {
   154  				continue
   155  			}
   156  
   157  			checkEntrypointRewrite(containers, &verifyEntrypointRewrite)
   158  			checkSupportContainer(containers, &verifySupportContainer)
   159  
   160  			if verifyEntrypointRewrite && verifySupportContainer {
   161  				break
   162  			}
   163  			tries++
   164  			if tries == maxTries {
   165  				break
   166  			}
   167  		}
   168  
   169  		if !verifyEntrypointRewrite {
   170  			t.Error("couldn't verify rewritten container")
   171  		}
   172  		if !verifySupportContainer {
   173  			t.Error("couldn't verify support container was created")
   174  		}
   175  	})
   176  }
   177  
   178  func checkEntrypointRewrite(containers []dockertypes.Container, found *bool) {
   179  	if *found {
   180  		return
   181  	}
   182  	for _, c := range containers {
   183  		if strings.Contains(c.Command, "docker-entrypoint.sh") {
   184  			*found = true
   185  		}
   186  	}
   187  }
   188  
   189  func checkSupportContainer(containers []dockertypes.Container, found *bool) {
   190  	if *found {
   191  		return
   192  	}
   193  	for _, c := range containers {
   194  		if strings.Contains(c.Image, "gcr.io/k8s-skaffold/skaffold-debug-support") {
   195  			*found = true
   196  		}
   197  	}
   198  }
   199  
   200  func TestFilterWithDebugging(t *testing.T) {
   201  	MarkIntegrationTest(t, CanRunWithoutGcp)
   202  	// `filter` currently expects to receive a digested yaml
   203  	renderedOutput := skaffold.Render("--digest-source=local").InDir("examples/getting-started").RunOrFailOutput(t)
   204  
   205  	testutil.Run(t, "no --build-artifacts should transform all images", func(t *testutil.T) {
   206  		transformedOutput := skaffold.Filter("--debugging").InDir("examples/getting-started").WithStdin(renderedOutput).RunOrFailOutput(t.T)
   207  		transformedYaml := string(transformedOutput)
   208  		if !strings.Contains(transformedYaml, "/dbg/go/bin/dlv") {
   209  			t.Error("transformed yaml seems to be missing debugging details", transformedYaml)
   210  		}
   211  	})
   212  
   213  	testutil.Run(t, "--build-artifacts=file should result in specific transforms", func(t *testutil.T) {
   214  		buildFile := t.TempFile("build.txt", []byte(`{"builds":[{"imageName":"doesnotexist","tag":"doesnotexist:notag"}]}`))
   215  		transformedOutput := skaffold.Filter("--debugging", "--build-artifacts="+buildFile).InDir("examples/getting-started").WithStdin(renderedOutput).RunOrFailOutput(t.T)
   216  		transformedYaml := string(transformedOutput)
   217  		if strings.Contains(transformedYaml, "/dbg/go/bin/dlv") {
   218  			t.Error("transformed yaml should not include debugging details", transformedYaml)
   219  		}
   220  	})
   221  }
   222  
   223  func TestDebugEventsRPC_StatusCheck(t *testing.T) {
   224  	t.Skipf("Disable the test dues to flakyness. https://github.com/GoogleContainerTools/skaffold/issues/7405")
   225  	MarkIntegrationTest(t, CanRunWithoutGcp)
   226  
   227  	// Run skaffold build first to fail quickly on a build failure
   228  	skaffold.Build().InDir("testdata/jib").RunOrFail(t)
   229  
   230  	ns, client := SetupNamespace(t)
   231  
   232  	rpcAddr := randomPort()
   233  	skaffold.Debug("--rpc-port", rpcAddr).InDir("testdata/jib").InNs(ns.Name).RunBackground(t)
   234  
   235  	waitForDebugEvent(t, client, rpcAddr)
   236  }
   237  
   238  func TestDebugEventsRPC_NoStatusCheck(t *testing.T) {
   239  	t.Skipf("Disable the test dues to flakyness. https://github.com/GoogleContainerTools/skaffold/issues/7405")
   240  	MarkIntegrationTest(t, CanRunWithoutGcp)
   241  
   242  	// Run skaffold build first to fail quickly on a build failure
   243  	skaffold.Build().InDir("testdata/jib").RunOrFail(t)
   244  
   245  	ns, client := SetupNamespace(t)
   246  
   247  	rpcAddr := randomPort()
   248  	skaffold.Debug("--rpc-port", rpcAddr, "--status-check=false").InDir("testdata/jib").InNs(ns.Name).RunBackground(t)
   249  
   250  	waitForDebugEvent(t, client, rpcAddr)
   251  }
   252  
   253  // nolint add lint back after https://github.com/GoogleContainerTools/skaffold/issues/7405
   254  func waitForDebugEvent(t *testing.T, client *NSKubernetesClient, rpcAddr string) {
   255  	client.WaitForPodsReady()
   256  
   257  	_, entries := apiEvents(t, rpcAddr)
   258  
   259  	timeout := time.After(1 * time.Minute)
   260  	for {
   261  		select {
   262  		case <-timeout:
   263  			t.Fatalf("timed out waiting for debugging event")
   264  		case entry := <-entries:
   265  			switch entry.Event.GetEventType().(type) {
   266  			case *proto.Event_DebuggingContainerEvent:
   267  				// success!
   268  				return
   269  			default:
   270  			}
   271  		}
   272  	}
   273  }