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

     1  /*
     2  Copyright 2021 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package debugging
    18  
    19  import (
    20  	"testing"
    21  
    22  	appsv1 "k8s.io/api/apps/v1"
    23  	batchv1 "k8s.io/api/batch/v1"
    24  	v1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug/types"
    30  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/debugging/adapter"
    31  	"github.com/GoogleContainerTools/skaffold/testutil"
    32  )
    33  
    34  func TestJdwpTransformerApply(t *testing.T) {
    35  	tests := []struct {
    36  		description   string
    37  		containerSpec v1.Container
    38  		configuration debug.ImageConfiguration
    39  		result        v1.Container
    40  		debugConfig   types.ContainerDebugConfiguration
    41  		image         string
    42  	}{
    43  		{
    44  			description:   "empty",
    45  			containerSpec: v1.Container{},
    46  			configuration: debug.ImageConfiguration{},
    47  			result: v1.Container{
    48  				Env:   []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
    49  				Ports: []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
    50  			},
    51  			debugConfig: types.ContainerDebugConfiguration{Runtime: "jvm", Ports: map[string]uint32{"jdwp": 5005}},
    52  		},
    53  		{
    54  			description: "existing port",
    55  			containerSpec: v1.Container{
    56  				Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}},
    57  			},
    58  			configuration: debug.ImageConfiguration{},
    59  			result: v1.Container{
    60  				Env:   []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
    61  				Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}, {Name: "jdwp", ContainerPort: 5005}},
    62  			},
    63  			debugConfig: types.ContainerDebugConfiguration{Runtime: "jvm", Ports: map[string]uint32{"jdwp": 5005}},
    64  		},
    65  		{
    66  			description: "existing jdwp spec",
    67  			containerSpec: v1.Container{
    68  				Env:   []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=n,quiet=y"}},
    69  				Ports: []v1.ContainerPort{{ContainerPort: 5005}},
    70  			},
    71  			configuration: debug.ImageConfiguration{Env: map[string]string{"JAVA_TOOL_OPTIONS": "-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=n,quiet=y"}},
    72  			result: v1.Container{
    73  				Env:   []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=n,quiet=y"}},
    74  				Ports: []v1.ContainerPort{{ContainerPort: 5005}, {Name: "jdwp", ContainerPort: 8000}},
    75  			},
    76  			debugConfig: types.ContainerDebugConfiguration{Runtime: "jvm", Ports: map[string]uint32{"jdwp": 8000}},
    77  		},
    78  		{
    79  			description: "existing jdwp port and JAVA_TOOL_OPTIONS",
    80  			containerSpec: v1.Container{
    81  				Env:   []v1.EnvVar{{Name: "FOO", Value: "BAR"}},
    82  				Ports: []v1.ContainerPort{{Name: "jdwp", ContainerPort: 8000}},
    83  			},
    84  			configuration: debug.ImageConfiguration{Env: map[string]string{"JAVA_TOOL_OPTIONS": "-Xms1g"}},
    85  			result: v1.Container{
    86  				Env:   []v1.EnvVar{{Name: "FOO", Value: "BAR"}, {Name: "JAVA_TOOL_OPTIONS", Value: "-Xms1g -agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
    87  				Ports: []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
    88  			},
    89  			debugConfig: types.ContainerDebugConfiguration{Runtime: "jvm", Ports: map[string]uint32{"jdwp": 5005}},
    90  		},
    91  	}
    92  	var identity debug.PortAllocator = func(port int32) int32 {
    93  		return port
    94  	}
    95  	for _, test := range tests {
    96  		testutil.Run(t, test.description, func(t *testutil.T) {
    97  			adapter := adapter.NewAdapter(&test.containerSpec)
    98  			config, image, err := debug.NewJDWPTransformer().Apply(adapter, test.configuration, identity, nil)
    99  			adapter.Apply()
   100  
   101  			// Apply never fails since there's always the option to set JAVA_TOOL_OPTIONS
   102  			t.CheckNil(err)
   103  			t.CheckDeepEqual(test.result, test.containerSpec)
   104  			t.CheckDeepEqual(test.debugConfig, config)
   105  			t.CheckDeepEqual(test.image, image)
   106  		})
   107  	}
   108  }
   109  
   110  func TestTransformManifestJVM(t *testing.T) {
   111  	int32p := func(x int32) *int32 { return &x }
   112  	tests := []struct {
   113  		description string
   114  		in          runtime.Object
   115  		transformed bool
   116  		out         runtime.Object
   117  	}{
   118  		{
   119  			"Pod with no transformable container",
   120  			&v1.Pod{
   121  				Spec: v1.PodSpec{Containers: []v1.Container{
   122  					{
   123  						Name:    "test",
   124  						Command: []string{"echo", "Hello World"},
   125  					},
   126  				}}},
   127  			false,
   128  			&v1.Pod{
   129  				Spec: v1.PodSpec{Containers: []v1.Container{
   130  					{
   131  						Name:    "test",
   132  						Command: []string{"echo", "Hello World"},
   133  					},
   134  				}}},
   135  		},
   136  		{
   137  			"Pod with Java container",
   138  			&v1.Pod{
   139  				Spec: v1.PodSpec{Containers: []v1.Container{
   140  					{
   141  						Name:    "test",
   142  						Command: []string{"java", "-jar", "foo.jar"},
   143  					},
   144  				}}},
   145  			true,
   146  			&v1.Pod{
   147  				ObjectMeta: metav1.ObjectMeta{
   148  					Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"jvm","ports":{"jdwp":5005}}}`},
   149  				},
   150  				Spec: v1.PodSpec{Containers: []v1.Container{
   151  					{
   152  						Name:    "test",
   153  						Command: []string{"java", "-jar", "foo.jar"},
   154  						Env:     []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
   155  						Ports:   []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
   156  					},
   157  				}}},
   158  		},
   159  		{
   160  			"Deployment with Java container",
   161  			&appsv1.Deployment{
   162  				Spec: appsv1.DeploymentSpec{
   163  					Replicas: int32p(2),
   164  					Template: v1.PodTemplateSpec{
   165  						Spec: v1.PodSpec{Containers: []v1.Container{
   166  							{
   167  								Name:    "test",
   168  								Command: []string{"java", "-jar", "foo.jar"},
   169  							},
   170  						}}}}},
   171  			true,
   172  			&appsv1.Deployment{
   173  				// ObjectMeta: metav1.ObjectMeta{
   174  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   175  				// },
   176  				Spec: appsv1.DeploymentSpec{
   177  					Replicas: int32p(1),
   178  					Template: v1.PodTemplateSpec{
   179  						ObjectMeta: metav1.ObjectMeta{
   180  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"jvm","ports":{"jdwp":5005}}}`},
   181  						},
   182  						Spec: v1.PodSpec{Containers: []v1.Container{
   183  							{
   184  								Name:    "test",
   185  								Command: []string{"java", "-jar", "foo.jar"},
   186  								Env:     []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
   187  								Ports:   []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
   188  							},
   189  						}}}}},
   190  		},
   191  		{
   192  			"ReplicaSet with Java container",
   193  			&appsv1.ReplicaSet{
   194  				Spec: appsv1.ReplicaSetSpec{
   195  					Replicas: int32p(2),
   196  					Template: v1.PodTemplateSpec{
   197  						Spec: v1.PodSpec{Containers: []v1.Container{
   198  							{
   199  								Name:    "test",
   200  								Command: []string{"java", "-jar", "foo.jar"},
   201  							},
   202  						}}}}},
   203  			true,
   204  			&appsv1.ReplicaSet{
   205  				// ObjectMeta: metav1.ObjectMeta{
   206  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   207  				// },
   208  				Spec: appsv1.ReplicaSetSpec{
   209  					Replicas: int32p(1),
   210  					Template: v1.PodTemplateSpec{
   211  						ObjectMeta: metav1.ObjectMeta{
   212  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"jvm","ports":{"jdwp":5005}}}`},
   213  						},
   214  						Spec: v1.PodSpec{Containers: []v1.Container{
   215  							{
   216  								Name:    "test",
   217  								Command: []string{"java", "-jar", "foo.jar"},
   218  								Env:     []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
   219  								Ports:   []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
   220  							},
   221  						}}}}},
   222  		},
   223  		{
   224  			"StatefulSet with Java container",
   225  			&appsv1.StatefulSet{
   226  				Spec: appsv1.StatefulSetSpec{
   227  					Replicas: int32p(2),
   228  					Template: v1.PodTemplateSpec{
   229  						Spec: v1.PodSpec{Containers: []v1.Container{
   230  							{
   231  								Name:    "test",
   232  								Command: []string{"java", "-jar", "foo.jar"},
   233  							},
   234  						}}}}},
   235  			true,
   236  			&appsv1.StatefulSet{
   237  				// ObjectMeta: metav1.ObjectMeta{
   238  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   239  				// },
   240  				Spec: appsv1.StatefulSetSpec{
   241  					Replicas: int32p(1),
   242  					Template: v1.PodTemplateSpec{
   243  						ObjectMeta: metav1.ObjectMeta{
   244  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"jvm","ports":{"jdwp":5005}}}`},
   245  						},
   246  						Spec: v1.PodSpec{Containers: []v1.Container{
   247  							{
   248  								Name:    "test",
   249  								Command: []string{"java", "-jar", "foo.jar"},
   250  								Env:     []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
   251  								Ports:   []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
   252  							},
   253  						}}}}},
   254  		},
   255  		{
   256  			"DaemonSet with Java container",
   257  			&appsv1.DaemonSet{
   258  				Spec: appsv1.DaemonSetSpec{
   259  					Template: v1.PodTemplateSpec{
   260  						Spec: v1.PodSpec{Containers: []v1.Container{
   261  							{
   262  								Name:    "test",
   263  								Command: []string{"java", "-jar", "foo.jar"},
   264  							},
   265  						}}}}},
   266  			true,
   267  			&appsv1.DaemonSet{
   268  				// ObjectMeta: metav1.ObjectMeta{
   269  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   270  				// },
   271  				Spec: appsv1.DaemonSetSpec{
   272  					Template: v1.PodTemplateSpec{
   273  						ObjectMeta: metav1.ObjectMeta{
   274  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"jvm","ports":{"jdwp":5005}}}`},
   275  						},
   276  						Spec: v1.PodSpec{Containers: []v1.Container{
   277  							{
   278  								Name:    "test",
   279  								Command: []string{"java", "-jar", "foo.jar"},
   280  								Env:     []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
   281  								Ports:   []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
   282  							},
   283  						}}}}},
   284  		},
   285  		{
   286  			"Job with Java container",
   287  			&batchv1.Job{
   288  				Spec: batchv1.JobSpec{
   289  					Template: v1.PodTemplateSpec{
   290  						Spec: v1.PodSpec{Containers: []v1.Container{
   291  							{
   292  								Name:    "test",
   293  								Command: []string{"java", "-jar", "foo.jar"},
   294  							},
   295  						}}}}},
   296  			true,
   297  			&batchv1.Job{
   298  				// ObjectMeta: metav1.ObjectMeta{
   299  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   300  				// },
   301  				Spec: batchv1.JobSpec{
   302  					Template: v1.PodTemplateSpec{
   303  						ObjectMeta: metav1.ObjectMeta{
   304  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"jvm","ports":{"jdwp":5005}}}`},
   305  						},
   306  						Spec: v1.PodSpec{Containers: []v1.Container{
   307  							{
   308  								Name:    "test",
   309  								Command: []string{"java", "-jar", "foo.jar"},
   310  								Env:     []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
   311  								Ports:   []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
   312  							},
   313  						}}}}},
   314  		},
   315  		{
   316  			"ReplicationController with Java container",
   317  			&v1.ReplicationController{
   318  				Spec: v1.ReplicationControllerSpec{
   319  					Replicas: int32p(2),
   320  					Template: &v1.PodTemplateSpec{
   321  						Spec: v1.PodSpec{Containers: []v1.Container{
   322  							{
   323  								Name:    "test",
   324  								Command: []string{"java", "-jar", "foo.jar"},
   325  							},
   326  						}}}}},
   327  			true,
   328  			&v1.ReplicationController{
   329  				// ObjectMeta: metav1.ObjectMeta{
   330  				//	Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`},
   331  				// },
   332  				Spec: v1.ReplicationControllerSpec{
   333  					Replicas: int32p(1),
   334  					Template: &v1.PodTemplateSpec{
   335  						ObjectMeta: metav1.ObjectMeta{
   336  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"jvm","ports":{"jdwp":5005}}}`},
   337  						},
   338  						Spec: v1.PodSpec{Containers: []v1.Container{
   339  							{
   340  								Name:    "test",
   341  								Command: []string{"java", "-jar", "foo.jar"},
   342  								Env:     []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
   343  								Ports:   []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
   344  							},
   345  						}}}}},
   346  		},
   347  		{
   348  			"PodList with Java and non-Java container",
   349  			&v1.PodList{
   350  				Items: []v1.Pod{
   351  					{
   352  						Spec: v1.PodSpec{Containers: []v1.Container{
   353  							{
   354  								Name:    "echo",
   355  								Command: []string{"echo", "Hello World"},
   356  							},
   357  						}}},
   358  					{
   359  						Spec: v1.PodSpec{Containers: []v1.Container{
   360  							{
   361  								Name:    "test",
   362  								Command: []string{"java", "-jar", "foo.jar"},
   363  							},
   364  						}}},
   365  				}},
   366  			true,
   367  			&v1.PodList{
   368  				Items: []v1.Pod{
   369  					{
   370  						Spec: v1.PodSpec{Containers: []v1.Container{
   371  							{
   372  								Name:    "echo",
   373  								Command: []string{"echo", "Hello World"},
   374  							},
   375  						}}},
   376  					{
   377  						ObjectMeta: metav1.ObjectMeta{
   378  							Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"jvm","ports":{"jdwp":5005}}}`},
   379  						},
   380  						Spec: v1.PodSpec{Containers: []v1.Container{
   381  							{
   382  								Name:    "test",
   383  								Command: []string{"java", "-jar", "foo.jar"},
   384  								Env:     []v1.EnvVar{{Name: "JAVA_TOOL_OPTIONS", Value: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y"}},
   385  								Ports:   []v1.ContainerPort{{Name: "jdwp", ContainerPort: 5005}},
   386  							},
   387  						}}},
   388  				}},
   389  		},
   390  	}
   391  	for _, test := range tests {
   392  		testutil.Run(t, test.description, func(t *testutil.T) {
   393  			value := test.in.DeepCopyObject()
   394  
   395  			retriever := func(image string) (debug.ImageConfiguration, error) {
   396  				return debug.ImageConfiguration{}, nil
   397  			}
   398  			result := transformManifest(value, retriever, "HELPERS")
   399  
   400  			t.CheckDeepEqual(test.transformed, result)
   401  			t.CheckDeepEqual(test.out, value)
   402  		})
   403  	}
   404  }