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

     1  /*
     2  Copyright 2021 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package debugging
    18  
    19  import (
    20  	"bytes"
    21  	"strings"
    22  	"testing"
    23  
    24  	v1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  
    27  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug/types"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
    30  	"github.com/GoogleContainerTools/skaffold/testutil"
    31  )
    32  
    33  // testTransformer is a simple transformer that applies to everything
    34  type testTransformer struct{}
    35  
    36  func (t testTransformer) IsApplicable(config debug.ImageConfiguration) bool {
    37  	return true
    38  }
    39  
    40  func (t testTransformer) Apply(adapter types.ContainerAdapter, config debug.ImageConfiguration, portAlloc debug.PortAllocator, overrideProtocols []string) (types.ContainerDebugConfiguration, string, error) {
    41  	port := portAlloc(9999)
    42  	container := adapter.GetContainer()
    43  	container.Ports = append(container.Ports, types.ContainerPort{Name: "test", ContainerPort: port})
    44  
    45  	testEnv := types.ContainerEnv{Order: []string{"KEY"}, Env: map[string]string{"KEY": "value"}}
    46  	container.Env = testEnv
    47  
    48  	return types.ContainerDebugConfiguration{Runtime: "test"}, "", nil
    49  }
    50  
    51  func TestPodEncodeDecode(t *testing.T) {
    52  	pod := &v1.Pod{
    53  		TypeMeta:   metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.Version, Kind: "Pod"},
    54  		ObjectMeta: metav1.ObjectMeta{Name: "podname"},
    55  		Spec:       v1.PodSpec{Containers: []v1.Container{{Name: "name1", Image: "image1"}}}}
    56  	b, err := encodeAsYaml(pod)
    57  	if err != nil {
    58  		t.Errorf("encodeAsYaml() failed: %v", err)
    59  		return
    60  	}
    61  	o, _, err := decodeFromYaml(b, nil, nil)
    62  	if err != nil {
    63  		t.Errorf("decodeFromYaml() failed: %v", err)
    64  		return
    65  	}
    66  	switch o := o.(type) {
    67  	case *v1.Pod:
    68  		testutil.CheckDeepEqual(t, "podname", o.ObjectMeta.Name)
    69  		testutil.CheckDeepEqual(t, 1, len(o.Spec.Containers))
    70  		testutil.CheckDeepEqual(t, "name1", o.Spec.Containers[0].Name)
    71  		testutil.CheckDeepEqual(t, "image1", o.Spec.Containers[0].Image)
    72  	default:
    73  		t.Errorf("decodeFromYaml() failed: expected *v1.Pod but got %T", o)
    74  	}
    75  }
    76  
    77  // TestSkipAnnotatedPodSpec verifies that transformPodSpec skips podspecs that have a
    78  // `debug.cloud.google.com/config` annotation.
    79  func TestSkipAnnotatedPodSpec(t *testing.T) {
    80  	tfm := testTransformer{}
    81  	debug.RegisterContainerTransformer(tfm)
    82  	defer debug.UnregisterContainerTransformer(tfm)
    83  
    84  	pod := v1.Pod{
    85  		TypeMeta:   metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.Version, Kind: "Pod"},
    86  		ObjectMeta: metav1.ObjectMeta{Name: "podname", Annotations: map[string]string{"debug.cloud.google.com/config": "{}"}},
    87  		Spec:       v1.PodSpec{Containers: []v1.Container{{Name: "name1", Image: "image1"}}}}
    88  
    89  	retriever := func(image string) (debug.ImageConfiguration, error) {
    90  		return debug.ImageConfiguration{WorkingDir: "/a/dir"}, nil
    91  	}
    92  
    93  	copy := pod
    94  	result := transformManifest(&pod, retriever, "HELPERS")
    95  	testutil.CheckDeepEqual(t, false, result)
    96  	testutil.CheckDeepEqual(t, copy, pod) // should be unchanged
    97  }
    98  
    99  func TestApplyDebuggingTransforms(t *testing.T) {
   100  	tfm := testTransformer{}
   101  	debug.RegisterContainerTransformer(tfm)
   102  	defer debug.UnregisterContainerTransformer(tfm)
   103  
   104  	tests := []struct {
   105  		description string
   106  		shouldErr   bool
   107  		in          string
   108  		out         string
   109  	}{
   110  		{
   111  			"Pod", false,
   112  			`apiVersion: v1
   113  kind: Pod
   114  metadata:
   115    name: pod
   116  spec:
   117    containers:
   118    - image: gcr.io/k8s-debug/debug-example:latest
   119      name: example
   120  `,
   121  			`apiVersion: v1
   122  kind: Pod
   123  metadata:
   124    annotations:
   125      debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   126    creationTimestamp: null
   127    name: pod
   128  spec:
   129    containers:
   130    - env:
   131      - name: KEY
   132        value: value
   133      image: gcr.io/k8s-debug/debug-example:latest
   134      name: example
   135      ports:
   136      - containerPort: 9999
   137        name: test
   138      resources: {}
   139  status: {}`,
   140  		},
   141  		{
   142  			"Deployment", false,
   143  			`apiVersion: apps/v1
   144  kind: Deployment
   145  metadata:
   146    name: my-app
   147  spec:
   148    replicas: 10
   149    selector:
   150      matchLabels:
   151        app: debug-app
   152    template:
   153      metadata:
   154        labels:
   155          app: debug-app
   156        name: debug-pod
   157      spec:
   158        containers:
   159        - image: gcr.io/k8s-debug/debug-example:latest
   160          name: example
   161  `,
   162  			`apiVersion: apps/v1
   163  kind: Deployment
   164  metadata:
   165    creationTimestamp: null
   166    name: my-app
   167  spec:
   168    replicas: 1
   169    selector:
   170      matchLabels:
   171        app: debug-app
   172    strategy: {}
   173    template:
   174      metadata:
   175        annotations:
   176          debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   177        creationTimestamp: null
   178        labels:
   179          app: debug-app
   180        name: debug-pod
   181      spec:
   182        containers:
   183        - env:
   184          - name: KEY
   185            value: value
   186          image: gcr.io/k8s-debug/debug-example:latest
   187          name: example
   188          ports:
   189          - containerPort: 9999
   190            name: test
   191          resources: {}
   192  status: {}`,
   193  		},
   194  		{
   195  			"ReplicaSet", false,
   196  			`apiVersion: apps/v1
   197  kind: ReplicaSet
   198  metadata:
   199    name: my-replicaset
   200  spec:
   201    replicas: 10
   202    selector:
   203      matchLabels:
   204        app: debug-app
   205    template:
   206      metadata:
   207        labels:
   208          app: debug-app
   209        name: debug-pod
   210      spec:
   211        containers:
   212        - image: gcr.io/k8s-debug/debug-example:latest
   213          name: example
   214  `,
   215  			`apiVersion: apps/v1
   216  kind: ReplicaSet
   217  metadata:
   218    creationTimestamp: null
   219    name: my-replicaset
   220  spec:
   221    replicas: 1
   222    selector:
   223      matchLabels:
   224        app: debug-app
   225    template:
   226      metadata:
   227        annotations:
   228          debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   229        creationTimestamp: null
   230        labels:
   231          app: debug-app
   232        name: debug-pod
   233      spec:
   234        containers:
   235        - env:
   236          - name: KEY
   237            value: value
   238          image: gcr.io/k8s-debug/debug-example:latest
   239          name: example
   240          ports:
   241          - containerPort: 9999
   242            name: test
   243          resources: {}
   244  status:
   245    replicas: 0`,
   246  		},
   247  		{
   248  			"StatefulSet", false,
   249  			`apiVersion: apps/v1
   250  kind: StatefulSet
   251  metadata:
   252    name: my-statefulset
   253  spec:
   254    replicas: 10
   255    selector:
   256      matchLabels:
   257        app: debug-app
   258    serviceName: service
   259    template:
   260      metadata:
   261        labels:
   262          app: debug-app
   263        name: debug-pod
   264      spec:
   265        containers:
   266        - image: gcr.io/k8s-debug/debug-example:latest
   267          name: example
   268  `,
   269  			`apiVersion: apps/v1
   270  kind: StatefulSet
   271  metadata:
   272    creationTimestamp: null
   273    name: my-statefulset
   274  spec:
   275    replicas: 1
   276    selector:
   277      matchLabels:
   278        app: debug-app
   279    serviceName: service
   280    template:
   281      metadata:
   282        annotations:
   283          debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   284        creationTimestamp: null
   285        labels:
   286          app: debug-app
   287        name: debug-pod
   288      spec:
   289        containers:
   290        - env:
   291          - name: KEY
   292            value: value
   293          image: gcr.io/k8s-debug/debug-example:latest
   294          name: example
   295          ports:
   296          - containerPort: 9999
   297            name: test
   298          resources: {}
   299    updateStrategy: {}
   300  status:
   301    availableReplicas: 0
   302    replicas: 0`,
   303  		},
   304  		{
   305  			"DaemonSet", false,
   306  			`apiVersion: apps/v1
   307  kind: DaemonSet
   308  metadata:
   309    name: my-daemonset
   310  spec:
   311    selector:
   312      matchLabels:
   313        app: debug-app
   314    template:
   315      metadata:
   316        labels:
   317          app: debug-app
   318        name: debug-pod
   319      spec:
   320        containers:
   321        - image: gcr.io/k8s-debug/debug-example:latest
   322          name: example
   323  `,
   324  			`apiVersion: apps/v1
   325  kind: DaemonSet
   326  metadata:
   327    creationTimestamp: null
   328    name: my-daemonset
   329  spec:
   330    selector:
   331      matchLabels:
   332        app: debug-app
   333    template:
   334      metadata:
   335        annotations:
   336          debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   337        creationTimestamp: null
   338        labels:
   339          app: debug-app
   340        name: debug-pod
   341      spec:
   342        containers:
   343        - env:
   344          - name: KEY
   345            value: value
   346          image: gcr.io/k8s-debug/debug-example:latest
   347          name: example
   348          ports:
   349          - containerPort: 9999
   350            name: test
   351          resources: {}
   352    updateStrategy: {}
   353  status:
   354    currentNumberScheduled: 0
   355    desiredNumberScheduled: 0
   356    numberMisscheduled: 0
   357    numberReady: 0`,
   358  		},
   359  		{
   360  			"Job", false,
   361  			`apiVersion: batch/v1
   362  kind: Job
   363  metadata:
   364    name: my-job
   365  spec:
   366    selector:
   367      matchLabels:
   368        app: debug-app
   369    template:
   370      metadata:
   371        labels:
   372          app: debug-app
   373        name: debug-pod
   374      spec:
   375        containers:
   376        - image: gcr.io/k8s-debug/debug-example:latest
   377          name: example
   378  `,
   379  			`apiVersion: batch/v1
   380  kind: Job
   381  metadata:
   382    creationTimestamp: null
   383    name: my-job
   384  spec:
   385    selector:
   386      matchLabels:
   387        app: debug-app
   388    template:
   389      metadata:
   390        annotations:
   391          debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   392        creationTimestamp: null
   393        labels:
   394          app: debug-app
   395        name: debug-pod
   396      spec:
   397        containers:
   398        - env:
   399          - name: KEY
   400            value: value
   401          image: gcr.io/k8s-debug/debug-example:latest
   402          name: example
   403          ports:
   404          - containerPort: 9999
   405            name: test
   406          resources: {}
   407  status: {}`,
   408  		},
   409  		{
   410  			"ReplicationController", false,
   411  			`apiVersion: v1
   412  kind: ReplicationController
   413  metadata:
   414    name: my-rc
   415  spec:
   416    replicas: 10
   417    selector:
   418      app: debug-app
   419    template:
   420      metadata:
   421        name: debug-pod
   422        labels:
   423          app: debug-app
   424      spec:
   425        containers:
   426        - image: gcr.io/k8s-debug/debug-example:latest
   427          name: example
   428  `,
   429  			`apiVersion: v1
   430  kind: ReplicationController
   431  metadata:
   432    creationTimestamp: null
   433    name: my-rc
   434  spec:
   435    replicas: 1
   436    selector:
   437      app: debug-app
   438    template:
   439      metadata:
   440        annotations:
   441          debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   442        creationTimestamp: null
   443        labels:
   444          app: debug-app
   445        name: debug-pod
   446      spec:
   447        containers:
   448        - env:
   449          - name: KEY
   450            value: value
   451          image: gcr.io/k8s-debug/debug-example:latest
   452          name: example
   453          ports:
   454          - containerPort: 9999
   455            name: test
   456          resources: {}
   457  status:
   458    replicas: 0`,
   459  		},
   460  		{
   461  			description: "skip unhandled yamls like crds",
   462  			shouldErr:   false,
   463  			in: `---
   464  apiVersion: openfaas.com/v1alpha2
   465  kind: Function
   466  metadata:
   467    name: myfunction
   468    namespace: openfaas-fn
   469  spec:
   470    name: myfunction
   471    image: myfunction`,
   472  			out: `---
   473  apiVersion: openfaas.com/v1alpha2
   474  kind: Function
   475  metadata:
   476    name: myfunction
   477    namespace: openfaas-fn
   478  spec:
   479    name: myfunction
   480    image: myfunction`,
   481  		},
   482  		{
   483  			"multiple objects", false,
   484  			`apiVersion: v1
   485  kind: Pod
   486  metadata:
   487    name: pod
   488  spec:
   489    containers:
   490    - image: gcr.io/k8s-debug/debug-example:latest
   491      name: example
   492  ---
   493  apiVersion: apps/v1
   494  kind: Deployment
   495  metadata:
   496    name: my-app
   497  spec:
   498    replicas: 10
   499    selector:
   500      matchLabels:
   501        app: debug-app
   502    template:
   503      metadata:
   504        labels:
   505          app: debug-app
   506        name: debug-pod
   507      spec:
   508        containers:
   509        - image: gcr.io/k8s-debug/debug-example:latest
   510          name: example
   511  `,
   512  			`apiVersion: v1
   513  kind: Pod
   514  metadata:
   515    annotations:
   516      debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   517    creationTimestamp: null
   518    name: pod
   519  spec:
   520    containers:
   521    - env:
   522      - name: KEY
   523        value: value
   524      image: gcr.io/k8s-debug/debug-example:latest
   525      name: example
   526      ports:
   527      - containerPort: 9999
   528        name: test
   529      resources: {}
   530  status: {}
   531  ---
   532  apiVersion: apps/v1
   533  kind: Deployment
   534  metadata:
   535    creationTimestamp: null
   536    name: my-app
   537  spec:
   538    replicas: 1
   539    selector:
   540      matchLabels:
   541        app: debug-app
   542    strategy: {}
   543    template:
   544      metadata:
   545        annotations:
   546          debug.cloud.google.com/config: '{"example":{"runtime":"test"}}'
   547        creationTimestamp: null
   548        labels:
   549          app: debug-app
   550        name: debug-pod
   551      spec:
   552        containers:
   553        - env:
   554          - name: KEY
   555            value: value
   556          image: gcr.io/k8s-debug/debug-example:latest
   557          name: example
   558          ports:
   559          - containerPort: 9999
   560            name: test
   561          resources: {}
   562  status: {}`,
   563  		},
   564  	}
   565  	for _, test := range tests {
   566  		testutil.Run(t, test.description, func(t *testutil.T) {
   567  			retriever := func(image string) (debug.ImageConfiguration, error) {
   568  				return debug.ImageConfiguration{}, nil
   569  			}
   570  
   571  			l, err := manifest.Load(bytes.NewReader([]byte(test.in)))
   572  			t.CheckError(false, err)
   573  			result, err := applyDebuggingTransforms(l, retriever, "HELPERS")
   574  
   575  			t.CheckErrorAndDeepEqual(test.shouldErr, err, test.out, result.String(), testutil.YamlObj(t.T))
   576  		})
   577  	}
   578  }
   579  
   580  func TestWorkingDir(t *testing.T) {
   581  	tfm := testTransformer{}
   582  	debug.RegisterContainerTransformer(tfm)
   583  	defer debug.UnregisterContainerTransformer(tfm)
   584  
   585  	pod := &v1.Pod{
   586  		TypeMeta:   metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.Version, Kind: "Pod"},
   587  		ObjectMeta: metav1.ObjectMeta{Name: "podname"},
   588  		Spec:       v1.PodSpec{Containers: []v1.Container{{Name: "name1", Image: "image1"}}}}
   589  
   590  	retriever := func(image string) (debug.ImageConfiguration, error) {
   591  		return debug.ImageConfiguration{WorkingDir: "/a/dir"}, nil
   592  	}
   593  
   594  	result := transformManifest(pod, retriever, "HELPERS")
   595  	testutil.CheckDeepEqual(t, true, result)
   596  	debugConfig := pod.ObjectMeta.Annotations["debug.cloud.google.com/config"]
   597  	testutil.CheckDeepEqual(t, true, strings.Contains(debugConfig, `"workingDir":"/a/dir"`))
   598  }
   599  
   600  func TestArtifactImage(t *testing.T) {
   601  	tfm := testTransformer{}
   602  	debug.RegisterContainerTransformer(tfm)
   603  	defer debug.UnregisterContainerTransformer(tfm)
   604  
   605  	pod := &v1.Pod{
   606  		TypeMeta:   metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.Version, Kind: "Pod"},
   607  		ObjectMeta: metav1.ObjectMeta{Name: "podname"},
   608  		Spec:       v1.PodSpec{Containers: []v1.Container{{Name: "name1", Image: "image1"}}}}
   609  
   610  	retriever := func(image string) (debug.ImageConfiguration, error) {
   611  		return debug.ImageConfiguration{Artifact: "gcr.io/random/image"}, nil
   612  	}
   613  
   614  	result := transformManifest(pod, retriever, "HELPERS")
   615  	testutil.CheckDeepEqual(t, true, result)
   616  	debugConfig := pod.ObjectMeta.Annotations["debug.cloud.google.com/config"]
   617  	testutil.CheckDeepEqual(t, true, strings.Contains(debugConfig, `"artifact":"gcr.io/random/image"`))
   618  }