github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/kubernetes/debugging/transform_python_test.go (about)

     1  /*
     2  Copyright 2021 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 debugging
    18  
    19  import (
    20  	"testing"
    21  
    22  	appsv1 "k8s.io/api/apps/v1"
    23  	batchv1 "k8s.io/api/batch/v1"
    24  	v1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug/types"
    30  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/debugging/adapter"
    31  	"github.com/GoogleContainerTools/skaffold/testutil"
    32  )
    33  
    34  func TestPythonTransformer_Apply(t *testing.T) {
    35  	tests := []struct {
    36  		description       string
    37  		containerSpec     v1.Container
    38  		configuration     debug.ImageConfiguration
    39  		overrideProtocols []string
    40  		shouldErr         bool
    41  		result            v1.Container
    42  		debugConfig       types.ContainerDebugConfiguration
    43  		image             string
    44  	}{
    45  		{
    46  			description:   "empty",
    47  			containerSpec: v1.Container{},
    48  			configuration: debug.ImageConfiguration{},
    49  			result:        v1.Container{},
    50  			shouldErr:     true,
    51  		},
    52  		{
    53  			description:   "basic",
    54  			containerSpec: v1.Container{},
    55  			configuration: debug.ImageConfiguration{Entrypoint: []string{"python"}},
    56  			result: v1.Container{
    57  				Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python"},
    58  				Ports:   []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
    59  			},
    60  			debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}},
    61  			image:       "python",
    62  		},
    63  		{
    64  			description:       "override protocol - pydevd, dap",
    65  			containerSpec:     v1.Container{},
    66  			configuration:     debug.ImageConfiguration{Entrypoint: []string{"python"}},
    67  			overrideProtocols: []string{"pydevd", "dap"},
    68  			result: v1.Container{
    69  				Command: []string{"/dbg/python/launcher", "--mode", "pydevd", "--port", "5678", "--", "python"},
    70  				Ports:   []v1.ContainerPort{{Name: "pydevd", ContainerPort: 5678}},
    71  			},
    72  			debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"pydevd": 5678}},
    73  			image:       "python",
    74  		},
    75  		{
    76  			description:       "override protocol - dap, pydevd",
    77  			containerSpec:     v1.Container{},
    78  			configuration:     debug.ImageConfiguration{Entrypoint: []string{"python"}},
    79  			overrideProtocols: []string{"dap", "pydevd"},
    80  			result: v1.Container{
    81  				Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python"},
    82  				Ports:   []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
    83  			},
    84  			debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}},
    85  			image:       "python",
    86  		},
    87  		{
    88  			description: "existing port",
    89  			containerSpec: v1.Container{
    90  				Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}},
    91  			},
    92  			configuration: debug.ImageConfiguration{Entrypoint: []string{"python"}},
    93  			result: v1.Container{
    94  				Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python"},
    95  				Ports:   []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}, {Name: "dap", ContainerPort: 5678}},
    96  			},
    97  			debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}},
    98  			image:       "python",
    99  		},
   100  		{
   101  			description:   "command not entrypoint",
   102  			containerSpec: v1.Container{},
   103  			configuration: debug.ImageConfiguration{Arguments: []string{"python"}},
   104  			result: v1.Container{
   105  				Args:  []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python"},
   106  				Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   107  			},
   108  			debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}},
   109  			image:       "python",
   110  		},
   111  		{
   112  			description:   "entrypoint with python env vars",
   113  			containerSpec: v1.Container{},
   114  			configuration: debug.ImageConfiguration{Entrypoint: []string{"foo"}, Env: map[string]string{"PYTHON_VERSION": "3"}},
   115  			result: v1.Container{
   116  				Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "foo"},
   117  				Ports:   []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   118  			},
   119  			debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}},
   120  			image:       "python",
   121  		},
   122  		{
   123  			description:   "command with python env vars",
   124  			containerSpec: v1.Container{},
   125  			configuration: debug.ImageConfiguration{Arguments: []string{"foo"}, Env: map[string]string{"PYTHON_VERSION": "3"}},
   126  			result: v1.Container{
   127  				Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--"},
   128  				Ports:   []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   129  			},
   130  			debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}},
   131  			image:       "python",
   132  		},
   133  	}
   134  	var identity debug.PortAllocator = func(port int32) int32 {
   135  		return port
   136  	}
   137  	for _, test := range tests {
   138  		testutil.Run(t, test.description, func(t *testutil.T) {
   139  			adapter := adapter.NewAdapter(&test.containerSpec)
   140  			config, image, err := debug.NewPythonTransformer().Apply(adapter, test.configuration, identity, test.overrideProtocols)
   141  			adapter.Apply()
   142  
   143  			t.CheckError(test.shouldErr, err)
   144  			t.CheckDeepEqual(test.result, test.containerSpec)
   145  			t.CheckDeepEqual(test.debugConfig, config)
   146  			t.CheckDeepEqual(test.image, image)
   147  		})
   148  	}
   149  }
   150  
   151  func TestTransformManifestPython(t *testing.T) {
   152  	int32p := func(x int32) *int32 { return &x }
   153  	tests := []struct {
   154  		description string
   155  		in          runtime.Object
   156  		transformed bool
   157  		out         runtime.Object
   158  	}{
   159  		{
   160  			"Pod with no transformable container",
   161  			&v1.Pod{
   162  				Spec: v1.PodSpec{Containers: []v1.Container{
   163  					{
   164  						Name:    "test",
   165  						Command: []string{"echo", "Hello World"},
   166  					},
   167  				}}},
   168  			false,
   169  			&v1.Pod{
   170  				Spec: v1.PodSpec{Containers: []v1.Container{
   171  					{
   172  						Name:    "test",
   173  						Command: []string{"echo", "Hello World"},
   174  					},
   175  				}}},
   176  		},
   177  		{
   178  			"Pod with ptvsd",
   179  			&v1.Pod{
   180  				Spec: v1.PodSpec{
   181  					Containers: []v1.Container{{
   182  						Name:    "test",
   183  						Command: []string{"python", "-mptvsd", "--host", "0.0.0.0", "--port", "5678", "foo.py"},
   184  					}},
   185  				}},
   186  			true,
   187  			&v1.Pod{
   188  				ObjectMeta: metav1.ObjectMeta{
   189  					Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   190  				},
   191  				Spec: v1.PodSpec{
   192  					Containers: []v1.Container{{
   193  						Name:    "test",
   194  						Command: []string{"python", "-mptvsd", "--host", "0.0.0.0", "--port", "5678", "foo.py"},
   195  						Ports:   []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   196  					}},
   197  				}},
   198  		},
   199  		{
   200  			"Pod with debugpy",
   201  			&v1.Pod{
   202  				Spec: v1.PodSpec{
   203  					Containers: []v1.Container{{
   204  						Name:    "test",
   205  						Command: []string{"python", "-mdebugpy", "--listen", "5678", "foo.py"},
   206  					}},
   207  				}},
   208  			true,
   209  			&v1.Pod{
   210  				ObjectMeta: metav1.ObjectMeta{
   211  					Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   212  				},
   213  				Spec: v1.PodSpec{
   214  					Containers: []v1.Container{{
   215  						Name:    "test",
   216  						Command: []string{"python", "-mdebugpy", "--listen", "5678", "foo.py"},
   217  						Ports:   []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   218  					}},
   219  				}},
   220  		},
   221  		{
   222  			"Pod with Python container",
   223  			&v1.Pod{
   224  				Spec: v1.PodSpec{
   225  					Containers: []v1.Container{{
   226  						Name:    "test",
   227  						Command: []string{"python", "foo.py"},
   228  					}},
   229  				}},
   230  			true,
   231  			&v1.Pod{
   232  				ObjectMeta: metav1.ObjectMeta{
   233  					Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   234  				},
   235  				Spec: v1.PodSpec{
   236  					Containers: []v1.Container{{
   237  						Name:         "test",
   238  						Command:      []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"},
   239  						Ports:        []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   240  						VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   241  					}},
   242  					InitContainers: []v1.Container{{
   243  						Name:         "install-python-debug-support",
   244  						Image:        "HELPERS/python",
   245  						VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   246  					}},
   247  					Volumes: []v1.Volume{{
   248  						Name:         "debugging-support-files",
   249  						VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
   250  					}},
   251  				}},
   252  		},
   253  		{
   254  			"Deployment with Python container",
   255  			&appsv1.Deployment{
   256  				Spec: appsv1.DeploymentSpec{
   257  					Replicas: int32p(2),
   258  					Template: v1.PodTemplateSpec{
   259  						Spec: v1.PodSpec{
   260  							Containers: []v1.Container{{
   261  								Name:    "test",
   262  								Command: []string{"python", "foo.py"},
   263  							}},
   264  						}}}},
   265  			true,
   266  			&appsv1.Deployment{
   267  				// ObjectMeta: metav1.ObjectMeta{
   268  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   269  				// },
   270  				Spec: appsv1.DeploymentSpec{
   271  					Replicas: int32p(1),
   272  					Template: v1.PodTemplateSpec{
   273  						ObjectMeta: metav1.ObjectMeta{
   274  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   275  						},
   276  						Spec: v1.PodSpec{
   277  							Containers: []v1.Container{{
   278  								Name:         "test",
   279  								Command:      []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"},
   280  								Ports:        []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   281  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   282  							}},
   283  							InitContainers: []v1.Container{{
   284  								Name:         "install-python-debug-support",
   285  								Image:        "HELPERS/python",
   286  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   287  							}},
   288  							Volumes: []v1.Volume{{
   289  								Name:         "debugging-support-files",
   290  								VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
   291  							}},
   292  						}}}},
   293  		},
   294  		{
   295  			"ReplicaSet with Python container",
   296  			&appsv1.ReplicaSet{
   297  				Spec: appsv1.ReplicaSetSpec{
   298  					Replicas: int32p(2),
   299  					Template: v1.PodTemplateSpec{
   300  						Spec: v1.PodSpec{
   301  							Containers: []v1.Container{{
   302  								Name:    "test",
   303  								Command: []string{"python", "foo.py"},
   304  							}},
   305  						}}}},
   306  			true,
   307  			&appsv1.ReplicaSet{
   308  				// ObjectMeta: metav1.ObjectMeta{
   309  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   310  				// },
   311  				Spec: appsv1.ReplicaSetSpec{
   312  					Replicas: int32p(1),
   313  					Template: v1.PodTemplateSpec{
   314  						ObjectMeta: metav1.ObjectMeta{
   315  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   316  						},
   317  						Spec: v1.PodSpec{
   318  							Containers: []v1.Container{{
   319  								Name:         "test",
   320  								Command:      []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"},
   321  								Ports:        []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   322  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   323  							}},
   324  							InitContainers: []v1.Container{{
   325  								Name:         "install-python-debug-support",
   326  								Image:        "HELPERS/python",
   327  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   328  							}},
   329  							Volumes: []v1.Volume{{
   330  								Name:         "debugging-support-files",
   331  								VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
   332  							}},
   333  						}}}},
   334  		},
   335  		{
   336  			"StatefulSet with Python container",
   337  			&appsv1.StatefulSet{
   338  				Spec: appsv1.StatefulSetSpec{
   339  					Replicas: int32p(2),
   340  					Template: v1.PodTemplateSpec{
   341  						Spec: v1.PodSpec{
   342  							Containers: []v1.Container{{
   343  								Name:    "test",
   344  								Command: []string{"python", "foo.py"},
   345  							}},
   346  						}}}},
   347  			true,
   348  			&appsv1.StatefulSet{
   349  				// ObjectMeta: metav1.ObjectMeta{
   350  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   351  				// },
   352  				Spec: appsv1.StatefulSetSpec{
   353  					Replicas: int32p(1),
   354  					Template: v1.PodTemplateSpec{
   355  						ObjectMeta: metav1.ObjectMeta{
   356  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   357  						},
   358  						Spec: v1.PodSpec{
   359  							Containers: []v1.Container{{
   360  								Name:         "test",
   361  								Command:      []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"},
   362  								Ports:        []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   363  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   364  							}},
   365  							InitContainers: []v1.Container{{
   366  								Name:         "install-python-debug-support",
   367  								Image:        "HELPERS/python",
   368  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   369  							}},
   370  							Volumes: []v1.Volume{{
   371  								Name:         "debugging-support-files",
   372  								VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
   373  							}},
   374  						}}}},
   375  		},
   376  		{
   377  			"DaemonSet with Python container",
   378  			&appsv1.DaemonSet{
   379  				Spec: appsv1.DaemonSetSpec{
   380  					Template: v1.PodTemplateSpec{
   381  						Spec: v1.PodSpec{
   382  							Containers: []v1.Container{{
   383  								Name:    "test",
   384  								Command: []string{"python", "foo.py"},
   385  							}},
   386  						}}}},
   387  			true,
   388  			&appsv1.DaemonSet{
   389  				// ObjectMeta: metav1.ObjectMeta{
   390  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   391  				// },
   392  				Spec: appsv1.DaemonSetSpec{
   393  					Template: v1.PodTemplateSpec{
   394  						ObjectMeta: metav1.ObjectMeta{
   395  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   396  						},
   397  						Spec: v1.PodSpec{
   398  							Containers: []v1.Container{{
   399  								Name:         "test",
   400  								Command:      []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"},
   401  								Ports:        []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   402  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   403  							}},
   404  							InitContainers: []v1.Container{{
   405  								Name:         "install-python-debug-support",
   406  								Image:        "HELPERS/python",
   407  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   408  							}},
   409  							Volumes: []v1.Volume{{
   410  								Name:         "debugging-support-files",
   411  								VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
   412  							}},
   413  						}}}},
   414  		},
   415  		{
   416  			"Job with Python container",
   417  			&batchv1.Job{
   418  				Spec: batchv1.JobSpec{
   419  					Template: v1.PodTemplateSpec{
   420  						Spec: v1.PodSpec{
   421  							Containers: []v1.Container{{
   422  								Name:    "test",
   423  								Command: []string{"python", "foo.py"},
   424  							}},
   425  						}}}},
   426  			true,
   427  			&batchv1.Job{
   428  				// ObjectMeta: metav1.ObjectMeta{
   429  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   430  				// },
   431  				Spec: batchv1.JobSpec{
   432  					Template: v1.PodTemplateSpec{
   433  						ObjectMeta: metav1.ObjectMeta{
   434  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   435  						},
   436  						Spec: v1.PodSpec{
   437  							Containers: []v1.Container{{
   438  								Name:         "test",
   439  								Command:      []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"},
   440  								Ports:        []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   441  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   442  							}},
   443  							InitContainers: []v1.Container{{
   444  								Name:         "install-python-debug-support",
   445  								Image:        "HELPERS/python",
   446  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   447  							}},
   448  							Volumes: []v1.Volume{{
   449  								Name:         "debugging-support-files",
   450  								VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
   451  							}},
   452  						}}}},
   453  		},
   454  		{
   455  			"ReplicationController with Python container",
   456  			&v1.ReplicationController{
   457  				Spec: v1.ReplicationControllerSpec{
   458  					Replicas: int32p(2),
   459  					Template: &v1.PodTemplateSpec{
   460  						Spec: v1.PodSpec{
   461  							Containers: []v1.Container{{
   462  								Name:    "test",
   463  								Command: []string{"python", "foo.py"},
   464  							}},
   465  						}}}},
   466  			true,
   467  			&v1.ReplicationController{
   468  				// ObjectMeta: metav1.ObjectMeta{
   469  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   470  				// },
   471  				Spec: v1.ReplicationControllerSpec{
   472  					Replicas: int32p(1),
   473  					Template: &v1.PodTemplateSpec{
   474  						ObjectMeta: metav1.ObjectMeta{
   475  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   476  						},
   477  						Spec: v1.PodSpec{
   478  							Containers: []v1.Container{{
   479  								Name:         "test",
   480  								Command:      []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"},
   481  								Ports:        []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   482  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   483  							}},
   484  							InitContainers: []v1.Container{{
   485  								Name:         "install-python-debug-support",
   486  								Image:        "HELPERS/python",
   487  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   488  							}},
   489  							Volumes: []v1.Volume{{
   490  								Name:         "debugging-support-files",
   491  								VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
   492  							}},
   493  						}}}},
   494  		},
   495  		{
   496  			"PodList with Python and non-Python container",
   497  			&v1.PodList{
   498  				Items: []v1.Pod{
   499  					{
   500  						Spec: v1.PodSpec{
   501  							Containers: []v1.Container{{
   502  								Name:    "echo",
   503  								Command: []string{"echo", "Hello World"},
   504  							}},
   505  						}},
   506  					{
   507  						Spec: v1.PodSpec{
   508  							Containers: []v1.Container{{
   509  								Name:    "test",
   510  								Command: []string{"python", "foo.py"},
   511  							}},
   512  						}},
   513  				}},
   514  			true,
   515  			&v1.PodList{
   516  				Items: []v1.Pod{
   517  					{
   518  						Spec: v1.PodSpec{
   519  							Containers: []v1.Container{{
   520  								Name:    "echo",
   521  								Command: []string{"echo", "Hello World"},
   522  							}},
   523  						}},
   524  					{
   525  						ObjectMeta: metav1.ObjectMeta{
   526  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`},
   527  						},
   528  						Spec: v1.PodSpec{
   529  							Containers: []v1.Container{{
   530  								Name:         "test",
   531  								Command:      []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"},
   532  								Ports:        []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}},
   533  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   534  							}},
   535  							InitContainers: []v1.Container{{
   536  								Name:         "install-python-debug-support",
   537  								Image:        "HELPERS/python",
   538  								VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}},
   539  							}},
   540  							Volumes: []v1.Volume{{
   541  								Name:         "debugging-support-files",
   542  								VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
   543  							}},
   544  						}},
   545  				}},
   546  		},
   547  	}
   548  	for _, test := range tests {
   549  		testutil.Run(t, test.description, func(t *testutil.T) {
   550  			value := test.in.DeepCopyObject()
   551  
   552  			retriever := func(image string) (debug.ImageConfiguration, error) {
   553  				return debug.ImageConfiguration{}, nil
   554  			}
   555  			result := transformManifest(value, retriever, "HELPERS")
   556  
   557  			t.CheckDeepEqual(test.transformed, result)
   558  			t.CheckDeepEqual(test.out, value)
   559  		})
   560  	}
   561  }