...

Source file src/k8s.io/kubectl/pkg/cmd/debug/debug_test.go

Documentation: k8s.io/kubectl/pkg/cmd/debug

     1  /*
     2  Copyright 2020 The Kubernetes 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 debug
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"github.com/google/go-cmp/cmp/cmpopts"
    29  	"github.com/spf13/cobra"
    30  
    31  	corev1 "k8s.io/api/core/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/cli-runtime/pkg/genericiooptions"
    34  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    35  	"k8s.io/utils/pointer"
    36  )
    37  
    38  func TestGenerateDebugContainer(t *testing.T) {
    39  	// Slightly less randomness for testing.
    40  	defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
    41  	var suffixCounter int
    42  	nameSuffixFunc = func(int) string {
    43  		suffixCounter++
    44  		return fmt.Sprint(suffixCounter)
    45  	}
    46  
    47  	for _, tc := range []struct {
    48  		name     string
    49  		opts     *DebugOptions
    50  		pod      *corev1.Pod
    51  		expected *corev1.EphemeralContainer
    52  	}{
    53  		{
    54  			name: "minimum fields",
    55  			opts: &DebugOptions{
    56  				Container:  "debugger",
    57  				Image:      "busybox",
    58  				PullPolicy: corev1.PullIfNotPresent,
    59  				Profile:    ProfileLegacy,
    60  			},
    61  			expected: &corev1.EphemeralContainer{
    62  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
    63  					Name:                     "debugger",
    64  					Image:                    "busybox",
    65  					ImagePullPolicy:          "IfNotPresent",
    66  					TerminationMessagePolicy: "File",
    67  				},
    68  			},
    69  		},
    70  		{
    71  			name: "namespace targeting",
    72  			opts: &DebugOptions{
    73  				Container:       "debugger",
    74  				Image:           "busybox",
    75  				PullPolicy:      corev1.PullIfNotPresent,
    76  				TargetContainer: "myapp",
    77  				Profile:         ProfileLegacy,
    78  			},
    79  			expected: &corev1.EphemeralContainer{
    80  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
    81  					Name:                     "debugger",
    82  					Image:                    "busybox",
    83  					ImagePullPolicy:          "IfNotPresent",
    84  					TerminationMessagePolicy: "File",
    85  				},
    86  				TargetContainerName: "myapp",
    87  			},
    88  		},
    89  		{
    90  			name: "debug args as container command",
    91  			opts: &DebugOptions{
    92  				Args:       []string{"/bin/echo", "one", "two", "three"},
    93  				Container:  "debugger",
    94  				Image:      "busybox",
    95  				PullPolicy: corev1.PullIfNotPresent,
    96  				Profile:    ProfileLegacy,
    97  			},
    98  			expected: &corev1.EphemeralContainer{
    99  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   100  					Name:                     "debugger",
   101  					Command:                  []string{"/bin/echo", "one", "two", "three"},
   102  					Image:                    "busybox",
   103  					ImagePullPolicy:          "IfNotPresent",
   104  					TerminationMessagePolicy: "File",
   105  				},
   106  			},
   107  		},
   108  		{
   109  			name: "debug args as container args",
   110  			opts: &DebugOptions{
   111  				ArgsOnly:   true,
   112  				Container:  "debugger",
   113  				Args:       []string{"echo", "one", "two", "three"},
   114  				Image:      "busybox",
   115  				PullPolicy: corev1.PullIfNotPresent,
   116  				Profile:    ProfileLegacy,
   117  			},
   118  			expected: &corev1.EphemeralContainer{
   119  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   120  					Name:                     "debugger",
   121  					Args:                     []string{"echo", "one", "two", "three"},
   122  					Image:                    "busybox",
   123  					ImagePullPolicy:          "IfNotPresent",
   124  					TerminationMessagePolicy: "File",
   125  				},
   126  			},
   127  		},
   128  		{
   129  			name: "random name generation",
   130  			opts: &DebugOptions{
   131  				Image:      "busybox",
   132  				PullPolicy: corev1.PullIfNotPresent,
   133  				Profile:    ProfileLegacy,
   134  			},
   135  			expected: &corev1.EphemeralContainer{
   136  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   137  					Name:                     "debugger-1",
   138  					Image:                    "busybox",
   139  					ImagePullPolicy:          "IfNotPresent",
   140  					TerminationMessagePolicy: "File",
   141  				},
   142  			},
   143  		},
   144  		{
   145  			name: "random name collision",
   146  			opts: &DebugOptions{
   147  				Image:      "busybox",
   148  				PullPolicy: corev1.PullIfNotPresent,
   149  				Profile:    ProfileLegacy,
   150  			},
   151  			pod: &corev1.Pod{
   152  				Spec: corev1.PodSpec{
   153  					Containers: []corev1.Container{
   154  						{
   155  							Name: "debugger-1",
   156  						},
   157  					},
   158  				},
   159  			},
   160  			expected: &corev1.EphemeralContainer{
   161  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   162  					Name:                     "debugger-2",
   163  					Image:                    "busybox",
   164  					ImagePullPolicy:          "IfNotPresent",
   165  					TerminationMessagePolicy: "File",
   166  				},
   167  			},
   168  		},
   169  		{
   170  			name: "pod with init containers",
   171  			opts: &DebugOptions{
   172  				Image:      "busybox",
   173  				PullPolicy: corev1.PullIfNotPresent,
   174  				Profile:    ProfileLegacy,
   175  			},
   176  			pod: &corev1.Pod{
   177  				Spec: corev1.PodSpec{
   178  					InitContainers: []corev1.Container{
   179  						{
   180  							Name: "init-container-1",
   181  						},
   182  						{
   183  							Name: "init-container-2",
   184  						},
   185  					},
   186  					Containers: []corev1.Container{
   187  						{
   188  							Name: "debugger",
   189  						},
   190  					},
   191  				},
   192  			},
   193  			expected: &corev1.EphemeralContainer{
   194  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   195  					Name:                     "debugger-1",
   196  					Image:                    "busybox",
   197  					ImagePullPolicy:          "IfNotPresent",
   198  					TerminationMessagePolicy: "File",
   199  				},
   200  			},
   201  		},
   202  		{
   203  			name: "pod with ephemeral containers",
   204  			opts: &DebugOptions{
   205  				Image:      "busybox",
   206  				PullPolicy: corev1.PullIfNotPresent,
   207  				Profile:    ProfileLegacy,
   208  			},
   209  			pod: &corev1.Pod{
   210  				Spec: corev1.PodSpec{
   211  					Containers: []corev1.Container{
   212  						{
   213  							Name: "debugger",
   214  						},
   215  					},
   216  					EphemeralContainers: []corev1.EphemeralContainer{
   217  						{
   218  							EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   219  								Name: "ephemeral-container-1",
   220  							},
   221  						},
   222  						{
   223  							EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   224  								Name: "ephemeral-container-2",
   225  							},
   226  						},
   227  					},
   228  				},
   229  			},
   230  			expected: &corev1.EphemeralContainer{
   231  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   232  					Name:                     "debugger-1",
   233  					Image:                    "busybox",
   234  					ImagePullPolicy:          "IfNotPresent",
   235  					TerminationMessagePolicy: "File",
   236  				},
   237  			},
   238  		},
   239  		{
   240  			name: "general profile",
   241  			opts: &DebugOptions{
   242  				Image:      "busybox",
   243  				PullPolicy: corev1.PullIfNotPresent,
   244  				Profile:    ProfileGeneral,
   245  			},
   246  			expected: &corev1.EphemeralContainer{
   247  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   248  					Name:                     "debugger-1",
   249  					Image:                    "busybox",
   250  					ImagePullPolicy:          corev1.PullIfNotPresent,
   251  					TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   252  					SecurityContext: &corev1.SecurityContext{
   253  						Capabilities: &corev1.Capabilities{
   254  							Add: []corev1.Capability{"SYS_PTRACE"},
   255  						},
   256  					},
   257  				},
   258  			},
   259  		},
   260  		{
   261  			name: "baseline profile",
   262  			opts: &DebugOptions{
   263  				Image:      "busybox",
   264  				PullPolicy: corev1.PullIfNotPresent,
   265  				Profile:    ProfileBaseline,
   266  			},
   267  			expected: &corev1.EphemeralContainer{
   268  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   269  					Name:                     "debugger-1",
   270  					Image:                    "busybox",
   271  					ImagePullPolicy:          corev1.PullIfNotPresent,
   272  					TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   273  				},
   274  			},
   275  		},
   276  		{
   277  			name: "restricted profile",
   278  			opts: &DebugOptions{
   279  				Image:      "busybox",
   280  				PullPolicy: corev1.PullIfNotPresent,
   281  				Profile:    ProfileRestricted,
   282  			},
   283  			expected: &corev1.EphemeralContainer{
   284  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   285  					Name:                     "debugger-1",
   286  					Image:                    "busybox",
   287  					ImagePullPolicy:          corev1.PullIfNotPresent,
   288  					TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   289  					SecurityContext: &corev1.SecurityContext{
   290  						RunAsNonRoot: pointer.Bool(true),
   291  						Capabilities: &corev1.Capabilities{
   292  							Drop: []corev1.Capability{"ALL"},
   293  						},
   294  						AllowPrivilegeEscalation: pointer.Bool(false),
   295  						SeccompProfile:           &corev1.SeccompProfile{Type: "RuntimeDefault"},
   296  					},
   297  				},
   298  			},
   299  		},
   300  		{
   301  			name: "netadmin profile",
   302  			opts: &DebugOptions{
   303  				Image:      "busybox",
   304  				PullPolicy: corev1.PullIfNotPresent,
   305  				Profile:    ProfileNetadmin,
   306  			},
   307  			expected: &corev1.EphemeralContainer{
   308  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   309  					Name:                     "debugger-1",
   310  					Image:                    "busybox",
   311  					ImagePullPolicy:          corev1.PullIfNotPresent,
   312  					TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   313  					SecurityContext: &corev1.SecurityContext{
   314  						Capabilities: &corev1.Capabilities{
   315  							Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
   316  						},
   317  					},
   318  				},
   319  			},
   320  		},
   321  		{
   322  			name: "sysadmin profile",
   323  			opts: &DebugOptions{
   324  				Image:      "busybox",
   325  				PullPolicy: corev1.PullIfNotPresent,
   326  				Profile:    ProfileSysadmin,
   327  			},
   328  			expected: &corev1.EphemeralContainer{
   329  				EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   330  					Name:                     "debugger-1",
   331  					Image:                    "busybox",
   332  					ImagePullPolicy:          corev1.PullIfNotPresent,
   333  					TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   334  					SecurityContext: &corev1.SecurityContext{
   335  						Privileged: pointer.Bool(true),
   336  					},
   337  				},
   338  			},
   339  		},
   340  	} {
   341  		t.Run(tc.name, func(t *testing.T) {
   342  			tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
   343  			suffixCounter = 0
   344  
   345  			if tc.pod == nil {
   346  				tc.pod = &corev1.Pod{}
   347  			}
   348  
   349  			applier, err := NewProfileApplier(tc.opts.Profile)
   350  			if err != nil {
   351  				t.Fatalf("failed to create profile applier: %s: %v", tc.opts.Profile, err)
   352  			}
   353  			tc.opts.Applier = applier
   354  
   355  			_, debugContainer, err := tc.opts.generateDebugContainer(tc.pod)
   356  			if err != nil {
   357  				t.Fatalf("fail to generate debug container: %v", err)
   358  			}
   359  			if diff := cmp.Diff(tc.expected, debugContainer); diff != "" {
   360  				t.Error("unexpected diff in generated object: (-want +got):\n", diff)
   361  			}
   362  		})
   363  	}
   364  }
   365  
   366  func TestGeneratePodCopyWithDebugContainer(t *testing.T) {
   367  	defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
   368  	var suffixCounter int
   369  	nameSuffixFunc = func(int) string {
   370  		suffixCounter++
   371  		return fmt.Sprint(suffixCounter)
   372  	}
   373  
   374  	for _, tc := range []struct {
   375  		name             string
   376  		opts             *DebugOptions
   377  		havePod, wantPod *corev1.Pod
   378  	}{
   379  		{
   380  			name: "basic",
   381  			opts: &DebugOptions{
   382  				CopyTo:     "debugger",
   383  				Container:  "debugger",
   384  				Image:      "busybox",
   385  				PullPolicy: corev1.PullIfNotPresent,
   386  				Profile:    ProfileLegacy,
   387  			},
   388  			havePod: &corev1.Pod{
   389  				ObjectMeta: metav1.ObjectMeta{
   390  					Name: "target",
   391  				},
   392  				Spec: corev1.PodSpec{
   393  					Containers: []corev1.Container{
   394  						{
   395  							Name: "debugger",
   396  						},
   397  					},
   398  					NodeName: "node-1",
   399  				},
   400  			},
   401  			wantPod: &corev1.Pod{
   402  				ObjectMeta: metav1.ObjectMeta{
   403  					Name: "debugger",
   404  				},
   405  				Spec: corev1.PodSpec{
   406  					Containers: []corev1.Container{
   407  						{
   408  							Name:            "debugger",
   409  							Image:           "busybox",
   410  							ImagePullPolicy: corev1.PullIfNotPresent,
   411  						},
   412  					},
   413  				},
   414  			},
   415  		},
   416  		{
   417  			name: "same node",
   418  			opts: &DebugOptions{
   419  				CopyTo:     "debugger",
   420  				Container:  "debugger",
   421  				Image:      "busybox",
   422  				PullPolicy: corev1.PullIfNotPresent,
   423  				SameNode:   true,
   424  				Profile:    ProfileLegacy,
   425  			},
   426  			havePod: &corev1.Pod{
   427  				ObjectMeta: metav1.ObjectMeta{
   428  					Name: "target",
   429  				},
   430  				Spec: corev1.PodSpec{
   431  					Containers: []corev1.Container{
   432  						{
   433  							Name: "debugger",
   434  						},
   435  					},
   436  					NodeName: "node-1",
   437  				},
   438  			},
   439  			wantPod: &corev1.Pod{
   440  				ObjectMeta: metav1.ObjectMeta{
   441  					Name: "debugger",
   442  				},
   443  				Spec: corev1.PodSpec{
   444  					Containers: []corev1.Container{
   445  						{
   446  							Name:            "debugger",
   447  							Image:           "busybox",
   448  							ImagePullPolicy: corev1.PullIfNotPresent,
   449  						},
   450  					},
   451  					NodeName: "node-1",
   452  				},
   453  			},
   454  		},
   455  		{
   456  			name: "metadata stripping",
   457  			opts: &DebugOptions{
   458  				CopyTo:     "debugger",
   459  				Container:  "debugger",
   460  				Image:      "busybox",
   461  				PullPolicy: corev1.PullIfNotPresent,
   462  				Profile:    ProfileLegacy,
   463  			},
   464  			havePod: &corev1.Pod{
   465  				ObjectMeta: metav1.ObjectMeta{
   466  					Name: "target",
   467  					Labels: map[string]string{
   468  						"app": "business",
   469  					},
   470  					Annotations: map[string]string{
   471  						"test": "test",
   472  					},
   473  					ResourceVersion:   "1",
   474  					CreationTimestamp: metav1.Time{Time: time.Now()},
   475  				},
   476  				Spec: corev1.PodSpec{
   477  					Containers: []corev1.Container{
   478  						{
   479  							Name: "debugger",
   480  						},
   481  					},
   482  				},
   483  			},
   484  			wantPod: &corev1.Pod{
   485  				ObjectMeta: metav1.ObjectMeta{
   486  					Name: "debugger",
   487  					Annotations: map[string]string{
   488  						"test": "test",
   489  					},
   490  				},
   491  				Spec: corev1.PodSpec{
   492  					Containers: []corev1.Container{
   493  						{
   494  							Name:            "debugger",
   495  							Image:           "busybox",
   496  							ImagePullPolicy: corev1.PullIfNotPresent,
   497  						},
   498  					},
   499  				},
   500  			},
   501  		},
   502  		{
   503  			name: "add a debug container",
   504  			opts: &DebugOptions{
   505  				CopyTo:     "debugger",
   506  				Container:  "debugger",
   507  				Image:      "busybox",
   508  				PullPolicy: corev1.PullIfNotPresent,
   509  				Profile:    ProfileLegacy,
   510  			},
   511  			havePod: &corev1.Pod{
   512  				ObjectMeta: metav1.ObjectMeta{
   513  					Name: "target",
   514  				},
   515  				Spec: corev1.PodSpec{
   516  					Containers: []corev1.Container{
   517  						{
   518  							Name: "business",
   519  						},
   520  					},
   521  				},
   522  			},
   523  			wantPod: &corev1.Pod{
   524  				ObjectMeta: metav1.ObjectMeta{
   525  					Name: "debugger",
   526  				},
   527  				Spec: corev1.PodSpec{
   528  					Containers: []corev1.Container{
   529  						{
   530  							Name: "business",
   531  						},
   532  						{
   533  							Name:                     "debugger",
   534  							Image:                    "busybox",
   535  							ImagePullPolicy:          corev1.PullIfNotPresent,
   536  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   537  						},
   538  					},
   539  				},
   540  			},
   541  		},
   542  		{
   543  			name: "customize envs",
   544  			opts: &DebugOptions{
   545  				CopyTo:     "debugger",
   546  				Container:  "debugger",
   547  				Image:      "busybox",
   548  				PullPolicy: corev1.PullIfNotPresent,
   549  				Env: []corev1.EnvVar{{
   550  					Name:  "TEST",
   551  					Value: "test",
   552  				}},
   553  				Profile: ProfileLegacy,
   554  			},
   555  			havePod: &corev1.Pod{
   556  				ObjectMeta: metav1.ObjectMeta{
   557  					Name: "target",
   558  				},
   559  				Spec: corev1.PodSpec{
   560  					Containers: []corev1.Container{
   561  						{
   562  							Name: "business",
   563  						},
   564  					},
   565  				},
   566  			},
   567  			wantPod: &corev1.Pod{
   568  				ObjectMeta: metav1.ObjectMeta{
   569  					Name: "debugger",
   570  				},
   571  				Spec: corev1.PodSpec{
   572  					Containers: []corev1.Container{
   573  						{
   574  							Name: "business",
   575  						},
   576  						{
   577  							Name:                     "debugger",
   578  							Image:                    "busybox",
   579  							ImagePullPolicy:          corev1.PullIfNotPresent,
   580  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   581  							Env: []corev1.EnvVar{{
   582  								Name:  "TEST",
   583  								Value: "test",
   584  							}},
   585  						},
   586  					},
   587  				},
   588  			},
   589  		},
   590  		{
   591  			name: "debug args as container command",
   592  			opts: &DebugOptions{
   593  				CopyTo:     "debugger",
   594  				Container:  "debugger",
   595  				Args:       []string{"/bin/echo", "one", "two", "three"},
   596  				Image:      "busybox",
   597  				PullPolicy: corev1.PullIfNotPresent,
   598  				Profile:    ProfileLegacy,
   599  			},
   600  			havePod: &corev1.Pod{
   601  				ObjectMeta: metav1.ObjectMeta{
   602  					Name: "target",
   603  				},
   604  				Spec: corev1.PodSpec{
   605  					Containers: []corev1.Container{
   606  						{
   607  							Name: "business",
   608  						},
   609  					},
   610  				},
   611  			},
   612  			wantPod: &corev1.Pod{
   613  				ObjectMeta: metav1.ObjectMeta{
   614  					Name: "debugger",
   615  				},
   616  				Spec: corev1.PodSpec{
   617  					Containers: []corev1.Container{
   618  						{
   619  							Name: "business",
   620  						},
   621  						{
   622  							Name:                     "debugger",
   623  							Image:                    "busybox",
   624  							Command:                  []string{"/bin/echo", "one", "two", "three"},
   625  							ImagePullPolicy:          corev1.PullIfNotPresent,
   626  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   627  						},
   628  					},
   629  				},
   630  			},
   631  		},
   632  		{
   633  			name: "debug args as container command",
   634  			opts: &DebugOptions{
   635  				CopyTo:     "debugger",
   636  				Container:  "debugger",
   637  				Args:       []string{"one", "two", "three"},
   638  				ArgsOnly:   true,
   639  				Image:      "busybox",
   640  				PullPolicy: corev1.PullIfNotPresent,
   641  				Profile:    ProfileLegacy,
   642  			},
   643  			havePod: &corev1.Pod{
   644  				ObjectMeta: metav1.ObjectMeta{
   645  					Name: "target",
   646  				},
   647  				Spec: corev1.PodSpec{
   648  					Containers: []corev1.Container{
   649  						{
   650  							Name: "business",
   651  						},
   652  					},
   653  				},
   654  			},
   655  			wantPod: &corev1.Pod{
   656  				ObjectMeta: metav1.ObjectMeta{
   657  					Name: "debugger",
   658  				},
   659  				Spec: corev1.PodSpec{
   660  					Containers: []corev1.Container{
   661  						{
   662  							Name: "business",
   663  						},
   664  						{
   665  							Name:                     "debugger",
   666  							Image:                    "busybox",
   667  							Args:                     []string{"one", "two", "three"},
   668  							ImagePullPolicy:          corev1.PullIfNotPresent,
   669  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   670  						},
   671  					},
   672  				},
   673  			},
   674  		},
   675  		{
   676  			name: "modify existing command to debug args",
   677  			opts: &DebugOptions{
   678  				CopyTo:     "debugger",
   679  				Container:  "debugger",
   680  				Args:       []string{"sleep", "1d"},
   681  				PullPolicy: corev1.PullIfNotPresent,
   682  				Profile:    ProfileLegacy,
   683  			},
   684  			havePod: &corev1.Pod{
   685  				ObjectMeta: metav1.ObjectMeta{
   686  					Name: "target",
   687  				},
   688  				Spec: corev1.PodSpec{
   689  					Containers: []corev1.Container{
   690  						{
   691  							Name:                     "debugger",
   692  							Command:                  []string{"echo"},
   693  							Image:                    "app",
   694  							Args:                     []string{"one", "two", "three"},
   695  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   696  						},
   697  					},
   698  				},
   699  			},
   700  			wantPod: &corev1.Pod{
   701  				ObjectMeta: metav1.ObjectMeta{
   702  					Name: "debugger",
   703  				},
   704  				Spec: corev1.PodSpec{
   705  					Containers: []corev1.Container{
   706  						{
   707  							Name:                     "debugger",
   708  							Image:                    "app",
   709  							Command:                  []string{"sleep", "1d"},
   710  							ImagePullPolicy:          corev1.PullIfNotPresent,
   711  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   712  						},
   713  					},
   714  				},
   715  			},
   716  		},
   717  		{
   718  			name: "random name",
   719  			opts: &DebugOptions{
   720  				CopyTo:     "debugger",
   721  				Image:      "busybox",
   722  				PullPolicy: corev1.PullIfNotPresent,
   723  				Profile:    ProfileLegacy,
   724  			},
   725  			havePod: &corev1.Pod{
   726  				ObjectMeta: metav1.ObjectMeta{
   727  					Name: "target",
   728  				},
   729  				Spec: corev1.PodSpec{
   730  					Containers: []corev1.Container{
   731  						{
   732  							Name: "business",
   733  						},
   734  					},
   735  				},
   736  			},
   737  			wantPod: &corev1.Pod{
   738  				ObjectMeta: metav1.ObjectMeta{
   739  					Name: "debugger",
   740  				},
   741  				Spec: corev1.PodSpec{
   742  					Containers: []corev1.Container{
   743  						{
   744  							Name: "business",
   745  						},
   746  						{
   747  							Name:                     "debugger-1",
   748  							Image:                    "busybox",
   749  							ImagePullPolicy:          corev1.PullIfNotPresent,
   750  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   751  						},
   752  					},
   753  				},
   754  			},
   755  		},
   756  		{
   757  			name: "random name collision",
   758  			opts: &DebugOptions{
   759  				CopyTo:     "debugger",
   760  				Image:      "busybox",
   761  				PullPolicy: corev1.PullIfNotPresent,
   762  				Profile:    ProfileLegacy,
   763  			},
   764  			havePod: &corev1.Pod{
   765  				ObjectMeta: metav1.ObjectMeta{
   766  					Name: "target",
   767  				},
   768  				Spec: corev1.PodSpec{
   769  					Containers: []corev1.Container{
   770  						{
   771  							Name: "debugger-1",
   772  						},
   773  					},
   774  				},
   775  			},
   776  			wantPod: &corev1.Pod{
   777  				ObjectMeta: metav1.ObjectMeta{
   778  					Name: "debugger",
   779  				},
   780  				Spec: corev1.PodSpec{
   781  					Containers: []corev1.Container{
   782  						{
   783  							Name: "debugger-1",
   784  						},
   785  						{
   786  							Name:                     "debugger-2",
   787  							Image:                    "busybox",
   788  							ImagePullPolicy:          corev1.PullIfNotPresent,
   789  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   790  						},
   791  					},
   792  				},
   793  			},
   794  		},
   795  		{
   796  			name: "pod with init containers",
   797  			opts: &DebugOptions{
   798  				CopyTo:     "debugger",
   799  				Image:      "busybox",
   800  				PullPolicy: corev1.PullIfNotPresent,
   801  				Profile:    ProfileLegacy,
   802  			},
   803  			havePod: &corev1.Pod{
   804  				ObjectMeta: metav1.ObjectMeta{
   805  					Name: "target",
   806  				},
   807  				Spec: corev1.PodSpec{
   808  					InitContainers: []corev1.Container{
   809  						{
   810  							Name: "init-container-1",
   811  						},
   812  						{
   813  							Name: "init-container-2",
   814  						},
   815  					},
   816  					Containers: []corev1.Container{
   817  						{
   818  							Name: "debugger-1",
   819  						},
   820  					},
   821  				},
   822  			},
   823  			wantPod: &corev1.Pod{
   824  				ObjectMeta: metav1.ObjectMeta{
   825  					Name: "debugger",
   826  				},
   827  				Spec: corev1.PodSpec{
   828  					InitContainers: []corev1.Container{
   829  						{
   830  							Name: "init-container-1",
   831  						},
   832  						{
   833  							Name: "init-container-2",
   834  						},
   835  					},
   836  					Containers: []corev1.Container{
   837  						{
   838  							Name: "debugger-1",
   839  						},
   840  						{
   841  							Name:                     "debugger-2",
   842  							Image:                    "busybox",
   843  							ImagePullPolicy:          corev1.PullIfNotPresent,
   844  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   845  						},
   846  					},
   847  				},
   848  			},
   849  		},
   850  		{
   851  			name: "pod with ephemeral containers",
   852  			opts: &DebugOptions{
   853  				CopyTo:     "debugger",
   854  				Image:      "busybox",
   855  				PullPolicy: corev1.PullIfNotPresent,
   856  				Profile:    ProfileLegacy,
   857  			},
   858  			havePod: &corev1.Pod{
   859  				ObjectMeta: metav1.ObjectMeta{
   860  					Name: "target",
   861  				},
   862  				Spec: corev1.PodSpec{
   863  					Containers: []corev1.Container{
   864  						{
   865  							Name: "debugger-1",
   866  						},
   867  					},
   868  					EphemeralContainers: []corev1.EphemeralContainer{
   869  						{
   870  							EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   871  								Name: "ephemeral-container-1",
   872  							},
   873  						},
   874  						{
   875  							EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   876  								Name: "ephemeral-container-2",
   877  							},
   878  						},
   879  					},
   880  				},
   881  			},
   882  			wantPod: &corev1.Pod{
   883  				ObjectMeta: metav1.ObjectMeta{
   884  					Name: "debugger",
   885  				},
   886  				Spec: corev1.PodSpec{
   887  					Containers: []corev1.Container{
   888  						{
   889  							Name: "debugger-1",
   890  						},
   891  						{
   892  							Name:                     "debugger-2",
   893  							Image:                    "busybox",
   894  							ImagePullPolicy:          corev1.PullIfNotPresent,
   895  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   896  						},
   897  					},
   898  				},
   899  			},
   900  		},
   901  		{
   902  			name: "shared process namespace",
   903  			opts: &DebugOptions{
   904  				CopyTo:                "debugger",
   905  				Container:             "debugger",
   906  				Image:                 "busybox",
   907  				PullPolicy:            corev1.PullIfNotPresent,
   908  				ShareProcesses:        true,
   909  				shareProcessedChanged: true,
   910  				Profile:               ProfileLegacy,
   911  			},
   912  			havePod: &corev1.Pod{
   913  				ObjectMeta: metav1.ObjectMeta{
   914  					Name: "target",
   915  				},
   916  				Spec: corev1.PodSpec{
   917  					Containers: []corev1.Container{
   918  						{
   919  							Name:                     "debugger",
   920  							ImagePullPolicy:          corev1.PullAlways,
   921  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   922  						},
   923  					},
   924  					NodeName: "node-1",
   925  				},
   926  			},
   927  			wantPod: &corev1.Pod{
   928  				ObjectMeta: metav1.ObjectMeta{
   929  					Name: "debugger",
   930  				},
   931  				Spec: corev1.PodSpec{
   932  					Containers: []corev1.Container{
   933  						{
   934  							Name:                     "debugger",
   935  							Image:                    "busybox",
   936  							ImagePullPolicy:          corev1.PullIfNotPresent,
   937  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   938  						},
   939  					},
   940  					ShareProcessNamespace: pointer.Bool(true),
   941  				},
   942  			},
   943  		},
   944  		{
   945  			name: "Change image for a named container",
   946  			opts: &DebugOptions{
   947  				Args:        []string{},
   948  				CopyTo:      "myapp-copy",
   949  				Container:   "app",
   950  				Image:       "busybox",
   951  				TargetNames: []string{"myapp"},
   952  				Profile:     ProfileLegacy,
   953  			},
   954  			havePod: &corev1.Pod{
   955  				ObjectMeta: metav1.ObjectMeta{Name: "myapp"},
   956  				Spec: corev1.PodSpec{
   957  					Containers: []corev1.Container{
   958  						{Name: "app", Image: "appimage"},
   959  						{Name: "sidecar", Image: "sidecarimage"},
   960  					},
   961  				},
   962  			},
   963  			wantPod: &corev1.Pod{
   964  				ObjectMeta: metav1.ObjectMeta{Name: "myapp-copy"},
   965  				Spec: corev1.PodSpec{
   966  					Containers: []corev1.Container{
   967  						{Name: "app", Image: "busybox"},
   968  						{Name: "sidecar", Image: "sidecarimage"},
   969  					},
   970  				},
   971  			},
   972  		},
   973  		{
   974  			name: "Change image for a named container with set-image",
   975  			opts: &DebugOptions{
   976  				CopyTo:    "myapp-copy",
   977  				Container: "app",
   978  				SetImages: map[string]string{"app": "busybox"},
   979  				Profile:   ProfileLegacy,
   980  			},
   981  			havePod: &corev1.Pod{
   982  				ObjectMeta: metav1.ObjectMeta{
   983  					Name: "myapp",
   984  				},
   985  				Spec: corev1.PodSpec{
   986  					Containers: []corev1.Container{
   987  						{Name: "app", Image: "appimage"},
   988  						{Name: "sidecar", Image: "sidecarimage"},
   989  					},
   990  				},
   991  			},
   992  			wantPod: &corev1.Pod{
   993  				ObjectMeta: metav1.ObjectMeta{
   994  					Name: "myapp-copy",
   995  				},
   996  				Spec: corev1.PodSpec{
   997  					Containers: []corev1.Container{
   998  						{Name: "app", Image: "busybox"},
   999  						{Name: "sidecar", Image: "sidecarimage"},
  1000  					},
  1001  				},
  1002  			},
  1003  		},
  1004  		{
  1005  			name: "Change image for all containers with set-image",
  1006  			opts: &DebugOptions{
  1007  				CopyTo:    "myapp-copy",
  1008  				SetImages: map[string]string{"*": "busybox"},
  1009  				Profile:   ProfileLegacy,
  1010  			},
  1011  			havePod: &corev1.Pod{
  1012  				ObjectMeta: metav1.ObjectMeta{
  1013  					Name: "myapp",
  1014  				},
  1015  				Spec: corev1.PodSpec{
  1016  					Containers: []corev1.Container{
  1017  						{Name: "app", Image: "appimage"},
  1018  						{Name: "sidecar", Image: "sidecarimage"},
  1019  					},
  1020  				},
  1021  			},
  1022  			wantPod: &corev1.Pod{
  1023  				ObjectMeta: metav1.ObjectMeta{
  1024  					Name: "myapp-copy",
  1025  				},
  1026  				Spec: corev1.PodSpec{
  1027  					Containers: []corev1.Container{
  1028  						{Name: "app", Image: "busybox"},
  1029  						{Name: "sidecar", Image: "busybox"},
  1030  					},
  1031  				},
  1032  			},
  1033  		},
  1034  		{
  1035  			name: "Change image for multiple containers with set-image",
  1036  			opts: &DebugOptions{
  1037  				CopyTo:    "myapp-copy",
  1038  				SetImages: map[string]string{"*": "busybox", "app": "app-debugger"},
  1039  				Profile:   ProfileLegacy,
  1040  			},
  1041  			havePod: &corev1.Pod{
  1042  				ObjectMeta: metav1.ObjectMeta{
  1043  					Name: "myapp",
  1044  				},
  1045  				Spec: corev1.PodSpec{
  1046  					Containers: []corev1.Container{
  1047  						{Name: "app", Image: "appimage"},
  1048  						{Name: "sidecar", Image: "sidecarimage"},
  1049  					},
  1050  				},
  1051  			},
  1052  			wantPod: &corev1.Pod{
  1053  				ObjectMeta: metav1.ObjectMeta{
  1054  					Name: "myapp-copy",
  1055  				},
  1056  				Spec: corev1.PodSpec{
  1057  					Containers: []corev1.Container{
  1058  						{Name: "app", Image: "app-debugger"},
  1059  						{Name: "sidecar", Image: "busybox"},
  1060  					},
  1061  				},
  1062  			},
  1063  		},
  1064  		{
  1065  			name: "Add interactive debug container minimal args",
  1066  			opts: &DebugOptions{
  1067  				Args:        []string{},
  1068  				Attach:      true,
  1069  				CopyTo:      "my-debugger",
  1070  				Image:       "busybox",
  1071  				Interactive: true,
  1072  				TargetNames: []string{"mypod"},
  1073  				TTY:         true,
  1074  				Profile:     ProfileLegacy,
  1075  			},
  1076  			havePod: &corev1.Pod{
  1077  				ObjectMeta: metav1.ObjectMeta{Name: "mypod"},
  1078  				Spec: corev1.PodSpec{
  1079  					Containers: []corev1.Container{
  1080  						{Name: "app", Image: "appimage"},
  1081  						{Name: "sidecar", Image: "sidecarimage"},
  1082  					},
  1083  				},
  1084  			},
  1085  			wantPod: &corev1.Pod{
  1086  				ObjectMeta: metav1.ObjectMeta{Name: "my-debugger"},
  1087  				Spec: corev1.PodSpec{
  1088  					Containers: []corev1.Container{
  1089  						{Name: "app", Image: "appimage"},
  1090  						{Name: "sidecar", Image: "sidecarimage"},
  1091  						{
  1092  							Name:                     "debugger-1",
  1093  							Image:                    "busybox",
  1094  							Stdin:                    true,
  1095  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1096  							TTY:                      true,
  1097  						},
  1098  					},
  1099  				},
  1100  			},
  1101  		},
  1102  		{
  1103  			name: "Pod copy: add container and also mutate images",
  1104  			opts: &DebugOptions{
  1105  				Args:        []string{},
  1106  				Attach:      true,
  1107  				CopyTo:      "my-debugger",
  1108  				Image:       "debian",
  1109  				Interactive: true,
  1110  				Namespace:   "default",
  1111  				SetImages: map[string]string{
  1112  					"app":     "app:debug",
  1113  					"sidecar": "sidecar:debug",
  1114  				},
  1115  				ShareProcesses: true,
  1116  				TargetNames:    []string{"mypod"},
  1117  				TTY:            true,
  1118  				Profile:        ProfileLegacy,
  1119  			},
  1120  			havePod: &corev1.Pod{
  1121  				ObjectMeta: metav1.ObjectMeta{Name: "mypod"},
  1122  				Spec: corev1.PodSpec{
  1123  					Containers: []corev1.Container{
  1124  						{Name: "app", Image: "appimage"},
  1125  						{Name: "sidecar", Image: "sidecarimage"},
  1126  					},
  1127  				},
  1128  			},
  1129  			wantPod: &corev1.Pod{
  1130  				ObjectMeta: metav1.ObjectMeta{Name: "my-debugger"},
  1131  				Spec: corev1.PodSpec{
  1132  					Containers: []corev1.Container{
  1133  						{Name: "app", Image: "app:debug"},
  1134  						{Name: "sidecar", Image: "sidecar:debug"},
  1135  						{
  1136  							Name:                     "debugger-1",
  1137  							Image:                    "debian",
  1138  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1139  							Stdin:                    true,
  1140  							TTY:                      true,
  1141  						},
  1142  					},
  1143  				},
  1144  			},
  1145  		},
  1146  		{
  1147  			name: "general profile",
  1148  			opts: &DebugOptions{
  1149  				CopyTo:     "debugger",
  1150  				Container:  "debugger",
  1151  				Image:      "busybox",
  1152  				PullPolicy: corev1.PullIfNotPresent,
  1153  				Profile:    ProfileGeneral,
  1154  			},
  1155  			havePod: &corev1.Pod{
  1156  				ObjectMeta: metav1.ObjectMeta{
  1157  					Name: "target",
  1158  				},
  1159  				Spec: corev1.PodSpec{
  1160  					Containers: []corev1.Container{
  1161  						{
  1162  							Name: "debugger",
  1163  						},
  1164  					},
  1165  					NodeName: "node-1",
  1166  				},
  1167  			},
  1168  			wantPod: &corev1.Pod{
  1169  				ObjectMeta: metav1.ObjectMeta{
  1170  					Name: "debugger",
  1171  				},
  1172  				Spec: corev1.PodSpec{
  1173  					Containers: []corev1.Container{
  1174  						{
  1175  							Name:            "debugger",
  1176  							Image:           "busybox",
  1177  							ImagePullPolicy: corev1.PullIfNotPresent,
  1178  							SecurityContext: &corev1.SecurityContext{
  1179  								Capabilities: &corev1.Capabilities{
  1180  									Add: []corev1.Capability{"SYS_PTRACE"},
  1181  								},
  1182  							},
  1183  						},
  1184  					},
  1185  					ShareProcessNamespace: pointer.Bool(true),
  1186  				},
  1187  			},
  1188  		},
  1189  		{
  1190  			name: "baseline profile",
  1191  			opts: &DebugOptions{
  1192  				CopyTo:     "debugger",
  1193  				Container:  "debugger",
  1194  				Image:      "busybox",
  1195  				PullPolicy: corev1.PullIfNotPresent,
  1196  				Profile:    ProfileBaseline,
  1197  			},
  1198  			havePod: &corev1.Pod{
  1199  				ObjectMeta: metav1.ObjectMeta{
  1200  					Name: "target",
  1201  				},
  1202  				Spec: corev1.PodSpec{
  1203  					Containers: []corev1.Container{
  1204  						{
  1205  							Name: "debugger",
  1206  						},
  1207  					},
  1208  					NodeName: "node-1",
  1209  				},
  1210  			},
  1211  			wantPod: &corev1.Pod{
  1212  				ObjectMeta: metav1.ObjectMeta{
  1213  					Name: "debugger",
  1214  				},
  1215  				Spec: corev1.PodSpec{
  1216  					Containers: []corev1.Container{
  1217  						{
  1218  							Name:            "debugger",
  1219  							Image:           "busybox",
  1220  							ImagePullPolicy: corev1.PullIfNotPresent,
  1221  						},
  1222  					},
  1223  					ShareProcessNamespace: pointer.Bool(true),
  1224  				},
  1225  			},
  1226  		},
  1227  		{
  1228  			name: "baseline profile not share process when user explicitly disables it",
  1229  			opts: &DebugOptions{
  1230  				CopyTo:                "debugger",
  1231  				Container:             "debugger",
  1232  				Image:                 "busybox",
  1233  				PullPolicy:            corev1.PullIfNotPresent,
  1234  				Profile:               ProfileBaseline,
  1235  				ShareProcesses:        false,
  1236  				shareProcessedChanged: true,
  1237  			},
  1238  			havePod: &corev1.Pod{
  1239  				ObjectMeta: metav1.ObjectMeta{
  1240  					Name: "target",
  1241  				},
  1242  				Spec: corev1.PodSpec{
  1243  					Containers: []corev1.Container{
  1244  						{
  1245  							Name: "debugger",
  1246  						},
  1247  					},
  1248  					NodeName: "node-1",
  1249  				},
  1250  			},
  1251  			wantPod: &corev1.Pod{
  1252  				ObjectMeta: metav1.ObjectMeta{
  1253  					Name: "debugger",
  1254  				},
  1255  				Spec: corev1.PodSpec{
  1256  					Containers: []corev1.Container{
  1257  						{
  1258  							Name:            "debugger",
  1259  							Image:           "busybox",
  1260  							ImagePullPolicy: corev1.PullIfNotPresent,
  1261  						},
  1262  					},
  1263  					ShareProcessNamespace: pointer.Bool(false),
  1264  				},
  1265  			},
  1266  		},
  1267  		{
  1268  			name: "restricted profile",
  1269  			opts: &DebugOptions{
  1270  				CopyTo:     "debugger",
  1271  				Container:  "debugger",
  1272  				Image:      "busybox",
  1273  				PullPolicy: corev1.PullIfNotPresent,
  1274  				Profile:    ProfileRestricted,
  1275  			},
  1276  			havePod: &corev1.Pod{
  1277  				ObjectMeta: metav1.ObjectMeta{
  1278  					Name: "target",
  1279  				},
  1280  				Spec: corev1.PodSpec{
  1281  					Containers: []corev1.Container{
  1282  						{
  1283  							Name: "debugger",
  1284  						},
  1285  					},
  1286  					NodeName: "node-1",
  1287  				},
  1288  			},
  1289  			wantPod: &corev1.Pod{
  1290  				ObjectMeta: metav1.ObjectMeta{
  1291  					Name: "debugger",
  1292  				},
  1293  				Spec: corev1.PodSpec{
  1294  					Containers: []corev1.Container{
  1295  						{
  1296  							Name:            "debugger",
  1297  							Image:           "busybox",
  1298  							ImagePullPolicy: corev1.PullIfNotPresent,
  1299  							SecurityContext: &corev1.SecurityContext{
  1300  								RunAsNonRoot: pointer.Bool(true),
  1301  								Capabilities: &corev1.Capabilities{
  1302  									Drop: []corev1.Capability{"ALL"},
  1303  								},
  1304  								AllowPrivilegeEscalation: pointer.Bool(false),
  1305  								SeccompProfile:           &corev1.SeccompProfile{Type: "RuntimeDefault"},
  1306  							},
  1307  						},
  1308  					},
  1309  					ShareProcessNamespace: pointer.Bool(true),
  1310  				},
  1311  			},
  1312  		},
  1313  		{
  1314  			name: "netadmin profile",
  1315  			opts: &DebugOptions{
  1316  				CopyTo:     "debugger",
  1317  				Container:  "debugger",
  1318  				Image:      "busybox",
  1319  				PullPolicy: corev1.PullIfNotPresent,
  1320  				Profile:    ProfileNetadmin,
  1321  			},
  1322  			havePod: &corev1.Pod{
  1323  				ObjectMeta: metav1.ObjectMeta{
  1324  					Name: "target",
  1325  				},
  1326  				Spec: corev1.PodSpec{
  1327  					Containers: []corev1.Container{
  1328  						{
  1329  							Name: "debugger",
  1330  						},
  1331  					},
  1332  					NodeName: "node-1",
  1333  				},
  1334  			},
  1335  			wantPod: &corev1.Pod{
  1336  				ObjectMeta: metav1.ObjectMeta{
  1337  					Name: "debugger",
  1338  				},
  1339  				Spec: corev1.PodSpec{
  1340  					Containers: []corev1.Container{
  1341  						{
  1342  							Name:            "debugger",
  1343  							Image:           "busybox",
  1344  							ImagePullPolicy: corev1.PullIfNotPresent,
  1345  							SecurityContext: &corev1.SecurityContext{
  1346  								Capabilities: &corev1.Capabilities{
  1347  									Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
  1348  								},
  1349  							},
  1350  						},
  1351  					},
  1352  					ShareProcessNamespace: pointer.Bool(true),
  1353  				},
  1354  			},
  1355  		},
  1356  	} {
  1357  		t.Run(tc.name, func(t *testing.T) {
  1358  			var err error
  1359  			tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
  1360  			if err != nil {
  1361  				t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
  1362  			}
  1363  			tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
  1364  			suffixCounter = 0
  1365  
  1366  			if tc.havePod == nil {
  1367  				tc.havePod = &corev1.Pod{}
  1368  			}
  1369  			gotPod, _, _ := tc.opts.generatePodCopyWithDebugContainer(tc.havePod)
  1370  			if diff := cmp.Diff(tc.wantPod, gotPod); diff != "" {
  1371  				t.Error("TestGeneratePodCopyWithDebugContainer: diff in generated object: (-want +got):\n", diff)
  1372  			}
  1373  		})
  1374  	}
  1375  }
  1376  
  1377  func TestGenerateNodeDebugPod(t *testing.T) {
  1378  	defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
  1379  	var suffixCounter int
  1380  	nameSuffixFunc = func(int) string {
  1381  		suffixCounter++
  1382  		return fmt.Sprint(suffixCounter)
  1383  	}
  1384  
  1385  	for _, tc := range []struct {
  1386  		name     string
  1387  		node     *corev1.Node
  1388  		opts     *DebugOptions
  1389  		expected *corev1.Pod
  1390  	}{
  1391  		{
  1392  			name: "minimum options",
  1393  			node: &corev1.Node{
  1394  				ObjectMeta: metav1.ObjectMeta{
  1395  					Name: "node-XXX",
  1396  				},
  1397  			},
  1398  			opts: &DebugOptions{
  1399  				Image:      "busybox",
  1400  				PullPolicy: corev1.PullIfNotPresent,
  1401  				Profile:    ProfileLegacy,
  1402  			},
  1403  			expected: &corev1.Pod{
  1404  				ObjectMeta: metav1.ObjectMeta{
  1405  					Name: "node-debugger-node-XXX-1",
  1406  				},
  1407  				Spec: corev1.PodSpec{
  1408  					Containers: []corev1.Container{
  1409  						{
  1410  							Name:                     "debugger",
  1411  							Image:                    "busybox",
  1412  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1413  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1414  							VolumeMounts: []corev1.VolumeMount{
  1415  								{
  1416  									MountPath: "/host",
  1417  									Name:      "host-root",
  1418  								},
  1419  							},
  1420  						},
  1421  					},
  1422  					HostIPC:       true,
  1423  					HostNetwork:   true,
  1424  					HostPID:       true,
  1425  					NodeName:      "node-XXX",
  1426  					RestartPolicy: corev1.RestartPolicyNever,
  1427  					Volumes: []corev1.Volume{
  1428  						{
  1429  							Name: "host-root",
  1430  							VolumeSource: corev1.VolumeSource{
  1431  								HostPath: &corev1.HostPathVolumeSource{Path: "/"},
  1432  							},
  1433  						},
  1434  					},
  1435  					Tolerations: []corev1.Toleration{
  1436  						{
  1437  							Operator: corev1.TolerationOpExists,
  1438  						},
  1439  					},
  1440  				},
  1441  			},
  1442  		},
  1443  		{
  1444  			name: "debug args as container command",
  1445  			node: &corev1.Node{
  1446  				ObjectMeta: metav1.ObjectMeta{
  1447  					Name: "node-XXX",
  1448  				},
  1449  			},
  1450  			opts: &DebugOptions{
  1451  				Args:       []string{"/bin/echo", "one", "two", "three"},
  1452  				Container:  "custom-debugger",
  1453  				Image:      "busybox",
  1454  				PullPolicy: corev1.PullIfNotPresent,
  1455  				Profile:    ProfileLegacy,
  1456  			},
  1457  			expected: &corev1.Pod{
  1458  				ObjectMeta: metav1.ObjectMeta{
  1459  					Name: "node-debugger-node-XXX-1",
  1460  				},
  1461  				Spec: corev1.PodSpec{
  1462  					Containers: []corev1.Container{
  1463  						{
  1464  							Name:                     "custom-debugger",
  1465  							Command:                  []string{"/bin/echo", "one", "two", "three"},
  1466  							Image:                    "busybox",
  1467  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1468  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1469  							VolumeMounts: []corev1.VolumeMount{
  1470  								{
  1471  									MountPath: "/host",
  1472  									Name:      "host-root",
  1473  								},
  1474  							},
  1475  						},
  1476  					},
  1477  					HostIPC:       true,
  1478  					HostNetwork:   true,
  1479  					HostPID:       true,
  1480  					NodeName:      "node-XXX",
  1481  					RestartPolicy: corev1.RestartPolicyNever,
  1482  					Volumes: []corev1.Volume{
  1483  						{
  1484  							Name: "host-root",
  1485  							VolumeSource: corev1.VolumeSource{
  1486  								HostPath: &corev1.HostPathVolumeSource{Path: "/"},
  1487  							},
  1488  						},
  1489  					},
  1490  					Tolerations: []corev1.Toleration{
  1491  						{
  1492  							Operator: corev1.TolerationOpExists,
  1493  						},
  1494  					},
  1495  				},
  1496  			},
  1497  		},
  1498  		{
  1499  			name: "debug args as container args",
  1500  			node: &corev1.Node{
  1501  				ObjectMeta: metav1.ObjectMeta{
  1502  					Name: "node-XXX",
  1503  				},
  1504  			},
  1505  			opts: &DebugOptions{
  1506  				ArgsOnly:   true,
  1507  				Container:  "custom-debugger",
  1508  				Args:       []string{"echo", "one", "two", "three"},
  1509  				Image:      "busybox",
  1510  				PullPolicy: corev1.PullIfNotPresent,
  1511  				Profile:    ProfileLegacy,
  1512  			},
  1513  			expected: &corev1.Pod{
  1514  				ObjectMeta: metav1.ObjectMeta{
  1515  					Name: "node-debugger-node-XXX-1",
  1516  				},
  1517  				Spec: corev1.PodSpec{
  1518  					Containers: []corev1.Container{
  1519  						{
  1520  							Name:                     "custom-debugger",
  1521  							Args:                     []string{"echo", "one", "two", "three"},
  1522  							Image:                    "busybox",
  1523  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1524  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1525  							VolumeMounts: []corev1.VolumeMount{
  1526  								{
  1527  									MountPath: "/host",
  1528  									Name:      "host-root",
  1529  								},
  1530  							},
  1531  						},
  1532  					},
  1533  					HostIPC:       true,
  1534  					HostNetwork:   true,
  1535  					HostPID:       true,
  1536  					NodeName:      "node-XXX",
  1537  					RestartPolicy: corev1.RestartPolicyNever,
  1538  					Volumes: []corev1.Volume{
  1539  						{
  1540  							Name: "host-root",
  1541  							VolumeSource: corev1.VolumeSource{
  1542  								HostPath: &corev1.HostPathVolumeSource{Path: "/"},
  1543  							},
  1544  						},
  1545  					},
  1546  					Tolerations: []corev1.Toleration{
  1547  						{
  1548  							Operator: corev1.TolerationOpExists,
  1549  						},
  1550  					},
  1551  				},
  1552  			},
  1553  		},
  1554  		{
  1555  			name: "general profile",
  1556  			node: &corev1.Node{
  1557  				ObjectMeta: metav1.ObjectMeta{
  1558  					Name: "node-XXX",
  1559  				},
  1560  			},
  1561  			opts: &DebugOptions{
  1562  				Image:      "busybox",
  1563  				PullPolicy: corev1.PullIfNotPresent,
  1564  				Profile:    ProfileGeneral,
  1565  			},
  1566  			expected: &corev1.Pod{
  1567  				ObjectMeta: metav1.ObjectMeta{
  1568  					Name: "node-debugger-node-XXX-1",
  1569  				},
  1570  				Spec: corev1.PodSpec{
  1571  					Containers: []corev1.Container{
  1572  						{
  1573  							Name:                     "debugger",
  1574  							Image:                    "busybox",
  1575  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1576  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1577  							VolumeMounts: []corev1.VolumeMount{
  1578  								{
  1579  									MountPath: "/host",
  1580  									Name:      "host-root",
  1581  								},
  1582  							},
  1583  						},
  1584  					},
  1585  					HostIPC:       true,
  1586  					HostNetwork:   true,
  1587  					HostPID:       true,
  1588  					NodeName:      "node-XXX",
  1589  					RestartPolicy: corev1.RestartPolicyNever,
  1590  					Volumes: []corev1.Volume{
  1591  						{
  1592  							Name: "host-root",
  1593  							VolumeSource: corev1.VolumeSource{
  1594  								HostPath: &corev1.HostPathVolumeSource{Path: "/"},
  1595  							},
  1596  						},
  1597  					},
  1598  					Tolerations: []corev1.Toleration{
  1599  						{
  1600  							Operator: corev1.TolerationOpExists,
  1601  						},
  1602  					},
  1603  				},
  1604  			},
  1605  		},
  1606  		{
  1607  			name: "baseline profile",
  1608  			node: &corev1.Node{
  1609  				ObjectMeta: metav1.ObjectMeta{
  1610  					Name: "node-XXX",
  1611  				},
  1612  			},
  1613  			opts: &DebugOptions{
  1614  				Image:      "busybox",
  1615  				PullPolicy: corev1.PullIfNotPresent,
  1616  				Profile:    ProfileBaseline,
  1617  			},
  1618  			expected: &corev1.Pod{
  1619  				ObjectMeta: metav1.ObjectMeta{
  1620  					Name: "node-debugger-node-XXX-1",
  1621  				},
  1622  				Spec: corev1.PodSpec{
  1623  					Containers: []corev1.Container{
  1624  						{
  1625  							Name:                     "debugger",
  1626  							Image:                    "busybox",
  1627  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1628  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1629  							VolumeMounts:             nil,
  1630  						},
  1631  					},
  1632  					HostIPC:       false,
  1633  					HostNetwork:   false,
  1634  					HostPID:       false,
  1635  					NodeName:      "node-XXX",
  1636  					RestartPolicy: corev1.RestartPolicyNever,
  1637  					Volumes:       nil,
  1638  					Tolerations: []corev1.Toleration{
  1639  						{
  1640  							Operator: corev1.TolerationOpExists,
  1641  						},
  1642  					},
  1643  				},
  1644  			},
  1645  		},
  1646  		{
  1647  			name: "restricted profile",
  1648  			node: &corev1.Node{
  1649  				ObjectMeta: metav1.ObjectMeta{
  1650  					Name: "node-XXX",
  1651  				},
  1652  			},
  1653  			opts: &DebugOptions{
  1654  				Image:      "busybox",
  1655  				PullPolicy: corev1.PullIfNotPresent,
  1656  				Profile:    ProfileRestricted,
  1657  			},
  1658  			expected: &corev1.Pod{
  1659  				ObjectMeta: metav1.ObjectMeta{
  1660  					Name: "node-debugger-node-XXX-1",
  1661  				},
  1662  				Spec: corev1.PodSpec{
  1663  					Containers: []corev1.Container{
  1664  						{
  1665  							Name:                     "debugger",
  1666  							Image:                    "busybox",
  1667  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1668  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1669  							VolumeMounts:             nil,
  1670  							SecurityContext: &corev1.SecurityContext{
  1671  								RunAsNonRoot: pointer.Bool(true),
  1672  								Capabilities: &corev1.Capabilities{
  1673  									Drop: []corev1.Capability{"ALL"},
  1674  								},
  1675  								AllowPrivilegeEscalation: pointer.Bool(false),
  1676  								SeccompProfile:           &corev1.SeccompProfile{Type: "RuntimeDefault"},
  1677  							},
  1678  						},
  1679  					},
  1680  					HostIPC:       false,
  1681  					HostNetwork:   false,
  1682  					HostPID:       false,
  1683  					NodeName:      "node-XXX",
  1684  					RestartPolicy: corev1.RestartPolicyNever,
  1685  					Volumes:       nil,
  1686  					Tolerations: []corev1.Toleration{
  1687  						{
  1688  							Operator: corev1.TolerationOpExists,
  1689  						},
  1690  					},
  1691  				},
  1692  			},
  1693  		},
  1694  		{
  1695  			name: "netadmin profile",
  1696  			node: &corev1.Node{
  1697  				ObjectMeta: metav1.ObjectMeta{
  1698  					Name: "node-XXX",
  1699  				},
  1700  			},
  1701  			opts: &DebugOptions{
  1702  				Image:      "busybox",
  1703  				PullPolicy: corev1.PullIfNotPresent,
  1704  				Profile:    ProfileNetadmin,
  1705  			},
  1706  			expected: &corev1.Pod{
  1707  				ObjectMeta: metav1.ObjectMeta{
  1708  					Name: "node-debugger-node-XXX-1",
  1709  				},
  1710  				Spec: corev1.PodSpec{
  1711  					Containers: []corev1.Container{
  1712  						{
  1713  							Name:                     "debugger",
  1714  							Image:                    "busybox",
  1715  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1716  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1717  							VolumeMounts:             nil,
  1718  							SecurityContext: &corev1.SecurityContext{
  1719  								Capabilities: &corev1.Capabilities{
  1720  									Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
  1721  								},
  1722  							},
  1723  						},
  1724  					},
  1725  					HostIPC:       true,
  1726  					HostNetwork:   true,
  1727  					HostPID:       true,
  1728  					NodeName:      "node-XXX",
  1729  					RestartPolicy: corev1.RestartPolicyNever,
  1730  					Volumes:       nil,
  1731  					Tolerations: []corev1.Toleration{
  1732  						{
  1733  							Operator: corev1.TolerationOpExists,
  1734  						},
  1735  					},
  1736  				},
  1737  			},
  1738  		},
  1739  	} {
  1740  		t.Run(tc.name, func(t *testing.T) {
  1741  			var err error
  1742  			tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
  1743  			if err != nil {
  1744  				t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
  1745  			}
  1746  			tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
  1747  			suffixCounter = 0
  1748  
  1749  			pod, err := tc.opts.generateNodeDebugPod(tc.node)
  1750  			if err != nil {
  1751  				t.Fatalf("Fail to generate node debug pod: %v", err)
  1752  			}
  1753  			if diff := cmp.Diff(tc.expected, pod); diff != "" {
  1754  				t.Error("unexpected diff in generated object: (-want +got):\n", diff)
  1755  			}
  1756  		})
  1757  	}
  1758  }
  1759  
  1760  func TestGenerateNodeDebugPodCustomProfile(t *testing.T) {
  1761  	for _, tc := range []struct {
  1762  		name     string
  1763  		node     *corev1.Node
  1764  		opts     *DebugOptions
  1765  		expected *corev1.Pod
  1766  	}{
  1767  		{
  1768  			name: "baseline profile",
  1769  			node: &corev1.Node{
  1770  				ObjectMeta: metav1.ObjectMeta{
  1771  					Name: "node-XXX",
  1772  				},
  1773  			},
  1774  			opts: &DebugOptions{
  1775  				Image:      "busybox",
  1776  				PullPolicy: corev1.PullIfNotPresent,
  1777  				Profile:    ProfileBaseline,
  1778  				CustomProfile: &corev1.Container{
  1779  					ImagePullPolicy: corev1.PullNever,
  1780  					Stdin:           true,
  1781  					TTY:             false,
  1782  					SecurityContext: &corev1.SecurityContext{
  1783  						Capabilities: &corev1.Capabilities{
  1784  							Drop: []corev1.Capability{"ALL"},
  1785  						},
  1786  						RunAsNonRoot: pointer.Bool(false),
  1787  					},
  1788  				},
  1789  			},
  1790  			expected: &corev1.Pod{
  1791  				ObjectMeta: metav1.ObjectMeta{
  1792  					Name: "node-debugger-node-XXX-1",
  1793  				},
  1794  				Spec: corev1.PodSpec{
  1795  					Containers: []corev1.Container{
  1796  						{
  1797  							Name:                     "debugger",
  1798  							Image:                    "busybox",
  1799  							ImagePullPolicy:          corev1.PullNever,
  1800  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1801  							VolumeMounts:             nil,
  1802  							Stdin:                    true,
  1803  							TTY:                      false,
  1804  							SecurityContext: &corev1.SecurityContext{
  1805  								RunAsNonRoot: pointer.Bool(false),
  1806  								Capabilities: &corev1.Capabilities{
  1807  									Drop: []corev1.Capability{"ALL"},
  1808  								},
  1809  							},
  1810  						},
  1811  					},
  1812  					HostIPC:       false,
  1813  					HostNetwork:   false,
  1814  					HostPID:       false,
  1815  					NodeName:      "node-XXX",
  1816  					RestartPolicy: corev1.RestartPolicyNever,
  1817  					Volumes:       nil,
  1818  					Tolerations: []corev1.Toleration{
  1819  						{
  1820  							Operator: corev1.TolerationOpExists,
  1821  						},
  1822  					},
  1823  				},
  1824  			},
  1825  		},
  1826  		{
  1827  			name: "restricted profile",
  1828  			node: &corev1.Node{
  1829  				ObjectMeta: metav1.ObjectMeta{
  1830  					Name: "node-XXX",
  1831  				},
  1832  			},
  1833  			opts: &DebugOptions{
  1834  				Image:      "busybox",
  1835  				PullPolicy: corev1.PullIfNotPresent,
  1836  				Profile:    ProfileRestricted,
  1837  				CustomProfile: &corev1.Container{
  1838  					ImagePullPolicy: corev1.PullNever,
  1839  					Stdin:           true,
  1840  					TTY:             false,
  1841  				},
  1842  			},
  1843  			expected: &corev1.Pod{
  1844  				ObjectMeta: metav1.ObjectMeta{
  1845  					Name: "node-debugger-node-XXX-1",
  1846  				},
  1847  				Spec: corev1.PodSpec{
  1848  					Containers: []corev1.Container{
  1849  						{
  1850  							Name:                     "debugger",
  1851  							Image:                    "busybox",
  1852  							ImagePullPolicy:          corev1.PullNever,
  1853  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1854  							VolumeMounts:             nil,
  1855  							Stdin:                    true,
  1856  							TTY:                      false,
  1857  							SecurityContext: &corev1.SecurityContext{
  1858  								RunAsNonRoot: pointer.Bool(true),
  1859  								Capabilities: &corev1.Capabilities{
  1860  									Drop: []corev1.Capability{"ALL"},
  1861  								},
  1862  								AllowPrivilegeEscalation: pointer.Bool(false),
  1863  								SeccompProfile:           &corev1.SeccompProfile{Type: "RuntimeDefault"},
  1864  							},
  1865  						},
  1866  					},
  1867  					HostIPC:       false,
  1868  					HostNetwork:   false,
  1869  					HostPID:       false,
  1870  					NodeName:      "node-XXX",
  1871  					RestartPolicy: corev1.RestartPolicyNever,
  1872  					Volumes:       nil,
  1873  					Tolerations: []corev1.Toleration{
  1874  						{
  1875  							Operator: corev1.TolerationOpExists,
  1876  						},
  1877  					},
  1878  				},
  1879  			},
  1880  		},
  1881  		{
  1882  			name: "netadmin profile",
  1883  			node: &corev1.Node{
  1884  				ObjectMeta: metav1.ObjectMeta{
  1885  					Name: "node-XXX",
  1886  				},
  1887  			},
  1888  			opts: &DebugOptions{
  1889  				Image:      "busybox",
  1890  				PullPolicy: corev1.PullIfNotPresent,
  1891  				Profile:    ProfileNetadmin,
  1892  				CustomProfile: &corev1.Container{
  1893  					Env: []corev1.EnvVar{
  1894  						{
  1895  							Name:  "TEST_KEY",
  1896  							Value: "TEST_VALUE",
  1897  						},
  1898  					},
  1899  				},
  1900  			},
  1901  			expected: &corev1.Pod{
  1902  				Spec: corev1.PodSpec{
  1903  					Containers: []corev1.Container{
  1904  						{
  1905  							Name:                     "debugger",
  1906  							Image:                    "busybox",
  1907  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1908  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1909  							Env: []corev1.EnvVar{
  1910  								{
  1911  									Name:  "TEST_KEY",
  1912  									Value: "TEST_VALUE",
  1913  								},
  1914  							},
  1915  							VolumeMounts: nil,
  1916  							SecurityContext: &corev1.SecurityContext{
  1917  								Capabilities: &corev1.Capabilities{
  1918  									Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
  1919  								},
  1920  							},
  1921  						},
  1922  					},
  1923  					HostIPC:       true,
  1924  					HostNetwork:   true,
  1925  					HostPID:       true,
  1926  					NodeName:      "node-XXX",
  1927  					RestartPolicy: corev1.RestartPolicyNever,
  1928  					Volumes:       nil,
  1929  					Tolerations: []corev1.Toleration{
  1930  						{
  1931  							Operator: corev1.TolerationOpExists,
  1932  						},
  1933  					},
  1934  				},
  1935  			},
  1936  		},
  1937  		{
  1938  			name: "sysadmin profile",
  1939  			node: &corev1.Node{
  1940  				ObjectMeta: metav1.ObjectMeta{
  1941  					Name: "node-XXX",
  1942  				},
  1943  			},
  1944  			opts: &DebugOptions{
  1945  				Image:      "busybox",
  1946  				PullPolicy: corev1.PullIfNotPresent,
  1947  				Profile:    ProfileSysadmin,
  1948  				CustomProfile: &corev1.Container{
  1949  					Env: []corev1.EnvVar{
  1950  						{
  1951  							Name:  "TEST_KEY",
  1952  							Value: "TEST_VALUE",
  1953  						},
  1954  					},
  1955  					VolumeMounts: []corev1.VolumeMount{
  1956  						{
  1957  							Name:      "host-root",
  1958  							ReadOnly:  true,
  1959  							MountPath: "/host",
  1960  						},
  1961  					},
  1962  				},
  1963  			},
  1964  			expected: &corev1.Pod{
  1965  				Spec: corev1.PodSpec{
  1966  					Containers: []corev1.Container{
  1967  						{
  1968  							Name:                     "debugger",
  1969  							Image:                    "busybox",
  1970  							ImagePullPolicy:          corev1.PullIfNotPresent,
  1971  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  1972  							Env: []corev1.EnvVar{
  1973  								{
  1974  									Name:  "TEST_KEY",
  1975  									Value: "TEST_VALUE",
  1976  								},
  1977  							},
  1978  							VolumeMounts: []corev1.VolumeMount{
  1979  								{
  1980  									Name:      "host-root",
  1981  									ReadOnly:  true,
  1982  									MountPath: "/host",
  1983  								},
  1984  							},
  1985  							SecurityContext: &corev1.SecurityContext{
  1986  								Privileged: pointer.Bool(true),
  1987  							},
  1988  						},
  1989  					},
  1990  					HostIPC:       true,
  1991  					HostNetwork:   true,
  1992  					HostPID:       true,
  1993  					NodeName:      "node-XXX",
  1994  					RestartPolicy: corev1.RestartPolicyNever,
  1995  					Volumes: []corev1.Volume{
  1996  						{
  1997  							Name: "host-root",
  1998  							VolumeSource: corev1.VolumeSource{
  1999  								HostPath: &corev1.HostPathVolumeSource{
  2000  									Path: "/",
  2001  								},
  2002  							},
  2003  						},
  2004  					},
  2005  					Tolerations: []corev1.Toleration{
  2006  						{
  2007  							Operator: corev1.TolerationOpExists,
  2008  						},
  2009  					},
  2010  				},
  2011  			},
  2012  		},
  2013  	} {
  2014  
  2015  		t.Run(tc.name, func(t *testing.T) {
  2016  			cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
  2017  				var err error
  2018  				tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
  2019  				if err != nil {
  2020  					t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
  2021  				}
  2022  				tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
  2023  
  2024  				pod, err := tc.opts.generateNodeDebugPod(tc.node)
  2025  				if err != nil {
  2026  					t.Fatalf("Fail to generate node debug pod: %v", err)
  2027  				}
  2028  				tc.expected.Name = pod.Name
  2029  				if diff := cmp.Diff(tc.expected, pod); diff != "" {
  2030  					t.Error("unexpected diff in generated object: (-want +got):\n", diff)
  2031  				}
  2032  			})
  2033  		})
  2034  	}
  2035  }
  2036  
  2037  func TestGenerateCopyDebugPodCustomProfile(t *testing.T) {
  2038  	for _, tc := range []struct {
  2039  		name     string
  2040  		copyPod  *corev1.Pod
  2041  		opts     *DebugOptions
  2042  		expected *corev1.Pod
  2043  	}{
  2044  		{
  2045  			name: "baseline profile",
  2046  			copyPod: &corev1.Pod{
  2047  				Spec: corev1.PodSpec{
  2048  					ServiceAccountName: "test",
  2049  					NodeName:           "test-node",
  2050  				},
  2051  			},
  2052  			opts: &DebugOptions{
  2053  				SameNode:   true,
  2054  				Image:      "busybox",
  2055  				PullPolicy: corev1.PullIfNotPresent,
  2056  				Profile:    ProfileBaseline,
  2057  				CustomProfile: &corev1.Container{
  2058  					ImagePullPolicy: corev1.PullNever,
  2059  					Stdin:           true,
  2060  					TTY:             false,
  2061  					SecurityContext: &corev1.SecurityContext{
  2062  						Capabilities: &corev1.Capabilities{
  2063  							Drop: []corev1.Capability{"ALL"},
  2064  						},
  2065  						RunAsNonRoot: pointer.Bool(false),
  2066  					},
  2067  				},
  2068  			},
  2069  			expected: &corev1.Pod{
  2070  				Spec: corev1.PodSpec{
  2071  					ServiceAccountName: "test",
  2072  					NodeName:           "test-node",
  2073  					Containers: []corev1.Container{
  2074  						{
  2075  							Image:                    "busybox",
  2076  							ImagePullPolicy:          corev1.PullNever,
  2077  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  2078  							VolumeMounts:             nil,
  2079  							Stdin:                    true,
  2080  							TTY:                      false,
  2081  							SecurityContext: &corev1.SecurityContext{
  2082  								RunAsNonRoot: pointer.Bool(false),
  2083  								Capabilities: &corev1.Capabilities{
  2084  									Drop: []corev1.Capability{"ALL"},
  2085  								},
  2086  							},
  2087  						},
  2088  					},
  2089  					HostIPC:               false,
  2090  					HostNetwork:           false,
  2091  					HostPID:               false,
  2092  					Volumes:               nil,
  2093  					ShareProcessNamespace: pointer.Bool(true),
  2094  				},
  2095  			},
  2096  		},
  2097  		{
  2098  			name: "restricted profile",
  2099  			copyPod: &corev1.Pod{
  2100  				Spec: corev1.PodSpec{
  2101  					ServiceAccountName: "test",
  2102  					NodeName:           "test-node",
  2103  				},
  2104  			},
  2105  			opts: &DebugOptions{
  2106  				SameNode:   true,
  2107  				Image:      "busybox",
  2108  				PullPolicy: corev1.PullIfNotPresent,
  2109  				Profile:    ProfileRestricted,
  2110  				CustomProfile: &corev1.Container{
  2111  					ImagePullPolicy: corev1.PullNever,
  2112  					Stdin:           true,
  2113  					TTY:             false,
  2114  					SecurityContext: &corev1.SecurityContext{
  2115  						Capabilities: &corev1.Capabilities{
  2116  							Drop: []corev1.Capability{"ALL"},
  2117  						},
  2118  						RunAsNonRoot: pointer.Bool(false),
  2119  					},
  2120  				},
  2121  			},
  2122  			expected: &corev1.Pod{
  2123  				Spec: corev1.PodSpec{
  2124  					ServiceAccountName: "test",
  2125  					NodeName:           "test-node",
  2126  					Containers: []corev1.Container{
  2127  						{
  2128  							Image:                    "busybox",
  2129  							ImagePullPolicy:          corev1.PullNever,
  2130  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  2131  							VolumeMounts:             nil,
  2132  							Stdin:                    true,
  2133  							TTY:                      false,
  2134  							SecurityContext: &corev1.SecurityContext{
  2135  								AllowPrivilegeEscalation: pointer.Bool(false),
  2136  								RunAsNonRoot:             pointer.Bool(false),
  2137  								Capabilities: &corev1.Capabilities{
  2138  									Drop: []corev1.Capability{"ALL"},
  2139  								},
  2140  								SeccompProfile: &corev1.SeccompProfile{
  2141  									Type:             corev1.SeccompProfileTypeRuntimeDefault,
  2142  									LocalhostProfile: nil,
  2143  								},
  2144  							},
  2145  						},
  2146  					},
  2147  					HostIPC:               false,
  2148  					HostNetwork:           false,
  2149  					HostPID:               false,
  2150  					Volumes:               nil,
  2151  					ShareProcessNamespace: pointer.Bool(true),
  2152  				},
  2153  			},
  2154  		},
  2155  		{
  2156  			name: "sysadmin profile",
  2157  			copyPod: &corev1.Pod{
  2158  				Spec: corev1.PodSpec{
  2159  					ServiceAccountName: "test",
  2160  					NodeName:           "test-node",
  2161  				},
  2162  			},
  2163  			opts: &DebugOptions{
  2164  				SameNode:   true,
  2165  				Image:      "busybox",
  2166  				PullPolicy: corev1.PullIfNotPresent,
  2167  				Profile:    ProfileRestricted,
  2168  				CustomProfile: &corev1.Container{
  2169  					ImagePullPolicy: corev1.PullNever,
  2170  					Stdin:           true,
  2171  					TTY:             false,
  2172  					SecurityContext: &corev1.SecurityContext{
  2173  						Capabilities: &corev1.Capabilities{
  2174  							Drop: []corev1.Capability{"ALL"},
  2175  						},
  2176  						RunAsNonRoot: pointer.Bool(false),
  2177  					},
  2178  				},
  2179  			},
  2180  			expected: &corev1.Pod{
  2181  				Spec: corev1.PodSpec{
  2182  					ServiceAccountName: "test",
  2183  					NodeName:           "test-node",
  2184  					Containers: []corev1.Container{
  2185  						{
  2186  							Image:                    "busybox",
  2187  							ImagePullPolicy:          corev1.PullNever,
  2188  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  2189  							VolumeMounts:             nil,
  2190  							Stdin:                    true,
  2191  							TTY:                      false,
  2192  							SecurityContext: &corev1.SecurityContext{
  2193  								AllowPrivilegeEscalation: pointer.Bool(false),
  2194  								RunAsNonRoot:             pointer.Bool(false),
  2195  								Capabilities: &corev1.Capabilities{
  2196  									Drop: []corev1.Capability{"ALL"},
  2197  								},
  2198  								SeccompProfile: &corev1.SeccompProfile{
  2199  									Type:             corev1.SeccompProfileTypeRuntimeDefault,
  2200  									LocalhostProfile: nil,
  2201  								},
  2202  							},
  2203  						},
  2204  					},
  2205  					HostIPC:               false,
  2206  					HostNetwork:           false,
  2207  					HostPID:               false,
  2208  					Volumes:               nil,
  2209  					ShareProcessNamespace: pointer.Bool(true),
  2210  				},
  2211  			},
  2212  		},
  2213  	} {
  2214  
  2215  		t.Run(tc.name, func(t *testing.T) {
  2216  			cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
  2217  				var err error
  2218  				tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
  2219  				if err != nil {
  2220  					t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
  2221  				}
  2222  				tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
  2223  
  2224  				pod, dc, err := tc.opts.generatePodCopyWithDebugContainer(tc.copyPod)
  2225  				if err != nil {
  2226  					t.Fatalf("Fail to generate node debug pod: %v", err)
  2227  				}
  2228  				tc.expected.Spec.Containers[0].Name = dc
  2229  				if diff := cmp.Diff(tc.expected, pod); diff != "" {
  2230  					t.Error("unexpected diff in generated object: (-want +got):\n", diff)
  2231  				}
  2232  			})
  2233  		})
  2234  	}
  2235  }
  2236  
  2237  func TestGenerateEphemeralDebugPodCustomProfile(t *testing.T) {
  2238  	for _, tc := range []struct {
  2239  		name     string
  2240  		copyPod  *corev1.Pod
  2241  		opts     *DebugOptions
  2242  		expected *corev1.Pod
  2243  	}{
  2244  		{
  2245  			name: "baseline profile",
  2246  			copyPod: &corev1.Pod{
  2247  				Spec: corev1.PodSpec{
  2248  					ServiceAccountName: "test",
  2249  					NodeName:           "test-node",
  2250  				},
  2251  			},
  2252  			opts: &DebugOptions{
  2253  				SameNode:   true,
  2254  				Image:      "busybox",
  2255  				PullPolicy: corev1.PullIfNotPresent,
  2256  				Profile:    ProfileBaseline,
  2257  				CustomProfile: &corev1.Container{
  2258  					ImagePullPolicy: corev1.PullNever,
  2259  					Stdin:           true,
  2260  					TTY:             false,
  2261  					SecurityContext: &corev1.SecurityContext{
  2262  						Capabilities: &corev1.Capabilities{
  2263  							Drop: []corev1.Capability{"ALL"},
  2264  						},
  2265  						RunAsNonRoot: pointer.Bool(false),
  2266  					},
  2267  				},
  2268  			},
  2269  			expected: &corev1.Pod{
  2270  				Spec: corev1.PodSpec{
  2271  					ServiceAccountName: "test",
  2272  					NodeName:           "test-node",
  2273  					EphemeralContainers: []corev1.EphemeralContainer{
  2274  						{
  2275  							EphemeralContainerCommon: corev1.EphemeralContainerCommon{
  2276  								Name:                     "debugger-1",
  2277  								Image:                    "busybox",
  2278  								ImagePullPolicy:          corev1.PullNever,
  2279  								TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  2280  								VolumeMounts:             nil,
  2281  								Stdin:                    true,
  2282  								TTY:                      false,
  2283  								SecurityContext: &corev1.SecurityContext{
  2284  									RunAsNonRoot: pointer.Bool(false),
  2285  									Capabilities: &corev1.Capabilities{
  2286  										Drop: []corev1.Capability{"ALL"},
  2287  									},
  2288  								},
  2289  							},
  2290  						},
  2291  					},
  2292  					HostIPC:     false,
  2293  					HostNetwork: false,
  2294  					HostPID:     false,
  2295  					Volumes:     nil,
  2296  				},
  2297  			},
  2298  		},
  2299  		{
  2300  			name: "restricted profile",
  2301  			copyPod: &corev1.Pod{
  2302  				Spec: corev1.PodSpec{
  2303  					ServiceAccountName: "test",
  2304  					NodeName:           "test-node",
  2305  				},
  2306  			},
  2307  			opts: &DebugOptions{
  2308  				SameNode:   true,
  2309  				Image:      "busybox",
  2310  				PullPolicy: corev1.PullIfNotPresent,
  2311  				Profile:    ProfileRestricted,
  2312  				CustomProfile: &corev1.Container{
  2313  					ImagePullPolicy: corev1.PullNever,
  2314  					Stdin:           true,
  2315  					TTY:             false,
  2316  					SecurityContext: &corev1.SecurityContext{
  2317  						Capabilities: &corev1.Capabilities{
  2318  							Drop: []corev1.Capability{"ALL"},
  2319  						},
  2320  						RunAsNonRoot: pointer.Bool(false),
  2321  					},
  2322  				},
  2323  			},
  2324  			expected: &corev1.Pod{
  2325  				Spec: corev1.PodSpec{
  2326  					ServiceAccountName: "test",
  2327  					NodeName:           "test-node",
  2328  					EphemeralContainers: []corev1.EphemeralContainer{
  2329  						{
  2330  							EphemeralContainerCommon: corev1.EphemeralContainerCommon{
  2331  								Name:                     "debugger-1",
  2332  								Image:                    "busybox",
  2333  								ImagePullPolicy:          corev1.PullNever,
  2334  								TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  2335  								VolumeMounts:             nil,
  2336  								Stdin:                    true,
  2337  								TTY:                      false,
  2338  								SecurityContext: &corev1.SecurityContext{
  2339  									AllowPrivilegeEscalation: pointer.Bool(false),
  2340  									RunAsNonRoot:             pointer.Bool(false),
  2341  									Capabilities: &corev1.Capabilities{
  2342  										Drop: []corev1.Capability{"ALL"},
  2343  									},
  2344  									SeccompProfile: &corev1.SeccompProfile{
  2345  										Type:             corev1.SeccompProfileTypeRuntimeDefault,
  2346  										LocalhostProfile: nil,
  2347  									},
  2348  								},
  2349  							},
  2350  						},
  2351  					},
  2352  					HostIPC:     false,
  2353  					HostNetwork: false,
  2354  					HostPID:     false,
  2355  					Volumes:     nil,
  2356  				},
  2357  			},
  2358  		},
  2359  		{
  2360  			name: "sysadmin profile",
  2361  			copyPod: &corev1.Pod{
  2362  				Spec: corev1.PodSpec{
  2363  					ServiceAccountName: "test",
  2364  					NodeName:           "test-node",
  2365  				},
  2366  			},
  2367  			opts: &DebugOptions{
  2368  				SameNode:   true,
  2369  				Image:      "busybox",
  2370  				PullPolicy: corev1.PullIfNotPresent,
  2371  				Profile:    ProfileRestricted,
  2372  				CustomProfile: &corev1.Container{
  2373  					ImagePullPolicy: corev1.PullNever,
  2374  					Stdin:           true,
  2375  					TTY:             false,
  2376  					SecurityContext: &corev1.SecurityContext{
  2377  						Capabilities: &corev1.Capabilities{
  2378  							Drop: []corev1.Capability{"ALL"},
  2379  						},
  2380  						RunAsNonRoot: pointer.Bool(false),
  2381  					},
  2382  				},
  2383  			},
  2384  			expected: &corev1.Pod{
  2385  				Spec: corev1.PodSpec{
  2386  					ServiceAccountName: "test",
  2387  					NodeName:           "test-node",
  2388  					EphemeralContainers: []corev1.EphemeralContainer{
  2389  						{
  2390  							EphemeralContainerCommon: corev1.EphemeralContainerCommon{
  2391  								Name:                     "debugger-1",
  2392  								Image:                    "busybox",
  2393  								ImagePullPolicy:          corev1.PullNever,
  2394  								TerminationMessagePolicy: corev1.TerminationMessageReadFile,
  2395  								VolumeMounts:             nil,
  2396  								Stdin:                    true,
  2397  								TTY:                      false,
  2398  								SecurityContext: &corev1.SecurityContext{
  2399  									AllowPrivilegeEscalation: pointer.Bool(false),
  2400  									RunAsNonRoot:             pointer.Bool(false),
  2401  									Capabilities: &corev1.Capabilities{
  2402  										Drop: []corev1.Capability{"ALL"},
  2403  									},
  2404  									SeccompProfile: &corev1.SeccompProfile{
  2405  										Type:             corev1.SeccompProfileTypeRuntimeDefault,
  2406  										LocalhostProfile: nil,
  2407  									},
  2408  								},
  2409  							},
  2410  						},
  2411  					},
  2412  					HostIPC:     false,
  2413  					HostNetwork: false,
  2414  					HostPID:     false,
  2415  					Volumes:     nil,
  2416  				},
  2417  			},
  2418  		},
  2419  	} {
  2420  
  2421  		t.Run(tc.name, func(t *testing.T) {
  2422  			cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
  2423  				var err error
  2424  				tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
  2425  				if err != nil {
  2426  					t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
  2427  				}
  2428  				tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
  2429  
  2430  				pod, ec, err := tc.opts.generateDebugContainer(tc.copyPod)
  2431  				if err != nil {
  2432  					t.Fatalf("Fail to generate node debug pod: %v", err)
  2433  				}
  2434  				tc.expected.Spec.EphemeralContainers[0].Name = ec.Name
  2435  				if diff := cmp.Diff(tc.expected, pod); diff != "" {
  2436  					t.Error("unexpected diff in generated object: (-want +got):\n", diff)
  2437  				}
  2438  			})
  2439  		})
  2440  	}
  2441  }
  2442  
  2443  func TestCompleteAndValidate(t *testing.T) {
  2444  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
  2445  	ioStreams, _, _, _ := genericiooptions.NewTestIOStreams()
  2446  	cmpFilter := cmp.FilterPath(func(p cmp.Path) bool {
  2447  		switch p.String() {
  2448  		// IOStreams contains unexported fields
  2449  		case "IOStreams", "Applier":
  2450  			return true
  2451  		}
  2452  		return false
  2453  	}, cmp.Ignore())
  2454  
  2455  	tests := []struct {
  2456  		name, args string
  2457  		wantOpts   *DebugOptions
  2458  		wantError  bool
  2459  	}{
  2460  		{
  2461  			name:      "No targets",
  2462  			args:      "--image=image",
  2463  			wantError: true,
  2464  		},
  2465  		{
  2466  			name:      "Invalid environment variables",
  2467  			args:      "--image=busybox --env=FOO mypod",
  2468  			wantError: true,
  2469  		},
  2470  		{
  2471  			name:      "Invalid image name",
  2472  			args:      "--image=image:label@deadbeef mypod",
  2473  			wantError: true,
  2474  		},
  2475  		{
  2476  			name:      "Invalid pull policy",
  2477  			args:      "--image=image --image-pull-policy=whenever-you-feel-like-it",
  2478  			wantError: true,
  2479  		},
  2480  		{
  2481  			name:      "TTY without stdin",
  2482  			args:      "--image=image --tty",
  2483  			wantError: true,
  2484  		},
  2485  		{
  2486  			name: "Set image pull policy",
  2487  			args: "--image=busybox --image-pull-policy=Always mypod",
  2488  			wantOpts: &DebugOptions{
  2489  				Args:           []string{},
  2490  				Image:          "busybox",
  2491  				Namespace:      "test",
  2492  				PullPolicy:     corev1.PullPolicy("Always"),
  2493  				ShareProcesses: true,
  2494  				Profile:        ProfileLegacy,
  2495  				TargetNames:    []string{"mypod"},
  2496  			},
  2497  		},
  2498  		{
  2499  			name: "Multiple targets",
  2500  			args: "--image=busybox mypod1 mypod2",
  2501  			wantOpts: &DebugOptions{
  2502  				Args:           []string{},
  2503  				Image:          "busybox",
  2504  				Namespace:      "test",
  2505  				ShareProcesses: true,
  2506  				Profile:        ProfileLegacy,
  2507  				TargetNames:    []string{"mypod1", "mypod2"},
  2508  			},
  2509  		},
  2510  		{
  2511  			name: "Arguments with dash",
  2512  			args: "--image=busybox mypod1 mypod2 -- echo 1 2",
  2513  			wantOpts: &DebugOptions{
  2514  				Args:           []string{"echo", "1", "2"},
  2515  				Image:          "busybox",
  2516  				Namespace:      "test",
  2517  				ShareProcesses: true,
  2518  				Profile:        ProfileLegacy,
  2519  				TargetNames:    []string{"mypod1", "mypod2"},
  2520  			},
  2521  		},
  2522  		{
  2523  			name: "Interactive no attach",
  2524  			args: "-ti --image=busybox --attach=false mypod",
  2525  			wantOpts: &DebugOptions{
  2526  				Args:           []string{},
  2527  				Attach:         false,
  2528  				Image:          "busybox",
  2529  				Interactive:    true,
  2530  				Namespace:      "test",
  2531  				ShareProcesses: true,
  2532  				Profile:        ProfileLegacy,
  2533  				TargetNames:    []string{"mypod"},
  2534  				TTY:            true,
  2535  			},
  2536  		},
  2537  		{
  2538  			name: "Set environment variables",
  2539  			args: "--image=busybox --env=FOO=BAR mypod",
  2540  			wantOpts: &DebugOptions{
  2541  				Args:           []string{},
  2542  				Env:            []corev1.EnvVar{{Name: "FOO", Value: "BAR"}},
  2543  				Image:          "busybox",
  2544  				Namespace:      "test",
  2545  				ShareProcesses: true,
  2546  				Profile:        ProfileLegacy,
  2547  				TargetNames:    []string{"mypod"},
  2548  			},
  2549  		},
  2550  		{
  2551  			name: "Ephemeral container: interactive session minimal args",
  2552  			args: "mypod -it --image=busybox",
  2553  			wantOpts: &DebugOptions{
  2554  				Args:           []string{},
  2555  				Attach:         true,
  2556  				Image:          "busybox",
  2557  				Interactive:    true,
  2558  				Namespace:      "test",
  2559  				ShareProcesses: true,
  2560  				Profile:        ProfileLegacy,
  2561  				TargetNames:    []string{"mypod"},
  2562  				TTY:            true,
  2563  			},
  2564  		},
  2565  		{
  2566  			name: "Ephemeral container: non-interactive debugger with image and name",
  2567  			args: "--image=myproj/debug-tools --image-pull-policy=Always -c debugger mypod",
  2568  			wantOpts: &DebugOptions{
  2569  				Args:           []string{},
  2570  				Container:      "debugger",
  2571  				Image:          "myproj/debug-tools",
  2572  				Namespace:      "test",
  2573  				PullPolicy:     corev1.PullPolicy("Always"),
  2574  				Profile:        ProfileLegacy,
  2575  				ShareProcesses: true,
  2576  				TargetNames:    []string{"mypod"},
  2577  			},
  2578  		},
  2579  		{
  2580  			name:      "Ephemeral container: no image specified",
  2581  			args:      "mypod",
  2582  			wantError: true,
  2583  		},
  2584  		{
  2585  			name:      "Ephemeral container: no image but args",
  2586  			args:      "mypod -- echo 1 2",
  2587  			wantError: true,
  2588  		},
  2589  		{
  2590  			name:      "Ephemeral container: replace not allowed",
  2591  			args:      "--replace --image=busybox mypod",
  2592  			wantError: true,
  2593  		},
  2594  		{
  2595  			name:      "Ephemeral container: same-node not allowed",
  2596  			args:      "--same-node --image=busybox mypod",
  2597  			wantError: true,
  2598  		},
  2599  		{
  2600  			name:      "Ephemeral container: incompatible with --set-image",
  2601  			args:      "--set-image=*=busybox mypod",
  2602  			wantError: true,
  2603  		},
  2604  		{
  2605  			name: "Pod copy: interactive debug container minimal args",
  2606  			args: "mypod -it --image=busybox --copy-to=my-debugger",
  2607  			wantOpts: &DebugOptions{
  2608  				Args:           []string{},
  2609  				Attach:         true,
  2610  				CopyTo:         "my-debugger",
  2611  				Image:          "busybox",
  2612  				Interactive:    true,
  2613  				Namespace:      "test",
  2614  				ShareProcesses: true,
  2615  				Profile:        ProfileLegacy,
  2616  				TargetNames:    []string{"mypod"},
  2617  				TTY:            true,
  2618  			},
  2619  		},
  2620  		{
  2621  			name: "Pod copy: non-interactive with debug container, image name and command",
  2622  			args: "mypod --image=busybox --container=my-container --copy-to=my-debugger -- sleep 1d",
  2623  			wantOpts: &DebugOptions{
  2624  				Args:           []string{"sleep", "1d"},
  2625  				Container:      "my-container",
  2626  				CopyTo:         "my-debugger",
  2627  				Image:          "busybox",
  2628  				Namespace:      "test",
  2629  				ShareProcesses: true,
  2630  				Profile:        ProfileLegacy,
  2631  				TargetNames:    []string{"mypod"},
  2632  			},
  2633  		},
  2634  		{
  2635  			name: "Pod copy: explicit attach",
  2636  			args: "mypod --image=busybox --copy-to=my-debugger --attach -- sleep 1d",
  2637  			wantOpts: &DebugOptions{
  2638  				Args:           []string{"sleep", "1d"},
  2639  				Attach:         true,
  2640  				CopyTo:         "my-debugger",
  2641  				Image:          "busybox",
  2642  				Namespace:      "test",
  2643  				ShareProcesses: true,
  2644  				Profile:        ProfileLegacy,
  2645  				TargetNames:    []string{"mypod"},
  2646  			},
  2647  		},
  2648  		{
  2649  			name: "Pod copy: replace single image of existing container",
  2650  			args: "mypod --image=busybox --container=my-container --copy-to=my-debugger",
  2651  			wantOpts: &DebugOptions{
  2652  				Args:           []string{},
  2653  				Container:      "my-container",
  2654  				CopyTo:         "my-debugger",
  2655  				Image:          "busybox",
  2656  				Namespace:      "test",
  2657  				ShareProcesses: true,
  2658  				Profile:        ProfileLegacy,
  2659  				TargetNames:    []string{"mypod"},
  2660  			},
  2661  		},
  2662  		{
  2663  			name: "Pod copy: mutate existing container images",
  2664  			args: "mypod --set-image=*=busybox,app=app-debugger --copy-to=my-debugger",
  2665  			wantOpts: &DebugOptions{
  2666  				Args:      []string{},
  2667  				CopyTo:    "my-debugger",
  2668  				Namespace: "test",
  2669  				SetImages: map[string]string{
  2670  					"*":   "busybox",
  2671  					"app": "app-debugger",
  2672  				},
  2673  				ShareProcesses: true,
  2674  				Profile:        ProfileLegacy,
  2675  				TargetNames:    []string{"mypod"},
  2676  			},
  2677  		},
  2678  		{
  2679  			name: "Pod copy: add container and also mutate images",
  2680  			args: "mypod -it --copy-to=my-debugger --image=debian --set-image=app=app:debug,sidecar=sidecar:debug",
  2681  			wantOpts: &DebugOptions{
  2682  				Args:        []string{},
  2683  				Attach:      true,
  2684  				CopyTo:      "my-debugger",
  2685  				Image:       "debian",
  2686  				Interactive: true,
  2687  				Namespace:   "test",
  2688  				SetImages: map[string]string{
  2689  					"app":     "app:debug",
  2690  					"sidecar": "sidecar:debug",
  2691  				},
  2692  				ShareProcesses: true,
  2693  				Profile:        ProfileLegacy,
  2694  				TargetNames:    []string{"mypod"},
  2695  				TTY:            true,
  2696  			},
  2697  		},
  2698  		{
  2699  			name: "Pod copy: change command",
  2700  			args: "mypod -it --copy-to=my-debugger --container=mycontainer -- sh",
  2701  			wantOpts: &DebugOptions{
  2702  				Attach:         true,
  2703  				Args:           []string{"sh"},
  2704  				Container:      "mycontainer",
  2705  				CopyTo:         "my-debugger",
  2706  				Interactive:    true,
  2707  				Namespace:      "test",
  2708  				ShareProcesses: true,
  2709  				Profile:        ProfileLegacy,
  2710  				TargetNames:    []string{"mypod"},
  2711  				TTY:            true,
  2712  			},
  2713  		},
  2714  		{
  2715  			name:      "Pod copy: no image specified",
  2716  			args:      "mypod -it --copy-to=my-debugger",
  2717  			wantError: true,
  2718  		},
  2719  		{
  2720  			name:      "Pod copy: args but no image specified",
  2721  			args:      "mypod --copy-to=my-debugger -- echo milo",
  2722  			wantError: true,
  2723  		},
  2724  		{
  2725  			name:      "Pod copy: --target not allowed",
  2726  			args:      "mypod --target --image=busybox --copy-to=my-debugger",
  2727  			wantError: true,
  2728  		},
  2729  		{
  2730  			name:      "Pod copy: invalid --set-image",
  2731  			args:      "mypod --set-image=*=SUPERGOODIMAGE#1!!!! --copy-to=my-debugger",
  2732  			wantError: true,
  2733  		},
  2734  		{
  2735  			name:      "Pod copy: specifying attach without existing or newly created container",
  2736  			args:      "mypod --set-image=*=busybox --copy-to=my-debugger --attach",
  2737  			wantError: true,
  2738  		},
  2739  		{
  2740  			name: "Node: interactive session minimal args",
  2741  			args: "node/mynode -it --image=busybox",
  2742  			wantOpts: &DebugOptions{
  2743  				Args:           []string{},
  2744  				Attach:         true,
  2745  				Image:          "busybox",
  2746  				Interactive:    true,
  2747  				Namespace:      "test",
  2748  				ShareProcesses: true,
  2749  				Profile:        ProfileLegacy,
  2750  				TargetNames:    []string{"node/mynode"},
  2751  				TTY:            true,
  2752  			},
  2753  		},
  2754  		{
  2755  			name:      "Node: no image specified",
  2756  			args:      "node/mynode -it",
  2757  			wantError: true,
  2758  		},
  2759  		{
  2760  			name:      "Node: --replace not allowed",
  2761  			args:      "--image=busybox --replace node/mynode",
  2762  			wantError: true,
  2763  		},
  2764  		{
  2765  			name:      "Node: --same-node not allowed",
  2766  			args:      "--image=busybox --same-node node/mynode",
  2767  			wantError: true,
  2768  		},
  2769  		{
  2770  			name:      "Node: --set-image not allowed",
  2771  			args:      "--image=busybox --set-image=*=busybox node/mynode",
  2772  			wantError: true,
  2773  		},
  2774  		{
  2775  			name:      "Node: --target not allowed",
  2776  			args:      "node/mynode --target --image=busybox",
  2777  			wantError: true,
  2778  		},
  2779  	}
  2780  
  2781  	for _, tc := range tests {
  2782  		t.Run(tc.name, func(t *testing.T) {
  2783  			opts := NewDebugOptions(ioStreams)
  2784  			var gotError error
  2785  			cmd := &cobra.Command{
  2786  				Run: func(cmd *cobra.Command, args []string) {
  2787  					gotError = opts.Complete(tf, cmd, args)
  2788  					if gotError != nil {
  2789  						return
  2790  					}
  2791  					gotError = opts.Validate()
  2792  				},
  2793  			}
  2794  			cmd.SetArgs(strings.Split(tc.args, " "))
  2795  			opts.AddFlags(cmd)
  2796  
  2797  			cmdError := cmd.Execute()
  2798  
  2799  			if tc.wantError {
  2800  				if cmdError != nil || gotError != nil {
  2801  					return
  2802  				}
  2803  				t.Fatalf("CompleteAndValidate got nil errors but wantError: %v", tc.wantError)
  2804  			} else if cmdError != nil {
  2805  				t.Fatalf("cmd.Execute got error '%v' executing test cobra.Command, wantError: %v", cmdError, tc.wantError)
  2806  			} else if gotError != nil {
  2807  				t.Fatalf("CompleteAndValidate got error: '%v', wantError: %v", gotError, tc.wantError)
  2808  			}
  2809  
  2810  			if diff := cmp.Diff(tc.wantOpts, opts, cmpFilter, cmpopts.IgnoreFields(DebugOptions{},
  2811  				"attachChanged", "shareProcessedChanged", "podClient", "WarningPrinter", "Applier", "explicitNamespace", "Builder", "AttachFunc")); diff != "" {
  2812  				t.Error("CompleteAndValidate unexpected diff in generated object: (-want +got):\n", diff)
  2813  			}
  2814  		})
  2815  	}
  2816  }
  2817  

View as plain text