github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/kubernetes/debugging/transform_nodejs_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 TestNodeTransformer_Apply(t *testing.T) { 35 // no shouldErr as Apply always succeeds 36 tests := []struct { 37 description string 38 containerSpec v1.Container 39 configuration debug.ImageConfiguration 40 result v1.Container 41 debugConfig types.ContainerDebugConfiguration 42 }{ 43 { 44 description: "empty", 45 containerSpec: v1.Container{}, 46 configuration: debug.ImageConfiguration{}, 47 result: v1.Container{ 48 Env: []v1.EnvVar{{Name: "NODE_OPTIONS", Value: "--inspect=0.0.0.0:9229"}, {Name: "PATH", Value: "/dbg/nodejs/bin"}}, 49 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 50 }, 51 debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, 52 }, 53 { 54 description: "entrypoint", 55 containerSpec: v1.Container{}, 56 configuration: debug.ImageConfiguration{Entrypoint: []string{"node"}}, 57 result: v1.Container{ 58 Command: []string{"node", "--inspect=0.0.0.0:9229"}, 59 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 60 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 61 }, 62 debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, 63 }, 64 { 65 description: "entrypoint with PATH", 66 containerSpec: v1.Container{}, 67 configuration: debug.ImageConfiguration{Entrypoint: []string{"node"}, Env: map[string]string{"PATH": "/usr/bin"}}, 68 result: v1.Container{ 69 Command: []string{"node", "--inspect=0.0.0.0:9229"}, 70 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin:/usr/bin"}}, 71 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 72 }, 73 debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, 74 }, 75 { 76 description: "existing port", 77 containerSpec: v1.Container{ 78 Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}}, 79 }, 80 configuration: debug.ImageConfiguration{Entrypoint: []string{"node"}}, 81 result: v1.Container{ 82 Command: []string{"node", "--inspect=0.0.0.0:9229"}, 83 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 84 Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}, {Name: "devtools", ContainerPort: 9229}}, 85 }, 86 debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, 87 }, 88 { 89 description: "existing devtools port", 90 containerSpec: v1.Container{ 91 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 4444}}, 92 }, 93 configuration: debug.ImageConfiguration{Entrypoint: []string{"node"}}, 94 result: v1.Container{ 95 Command: []string{"node", "--inspect=0.0.0.0:9229"}, 96 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 97 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 98 }, 99 debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, 100 }, 101 { 102 description: "command not entrypoint", 103 containerSpec: v1.Container{}, 104 configuration: debug.ImageConfiguration{Arguments: []string{"node"}}, 105 result: v1.Container{ 106 Args: []string{"node", "--inspect=0.0.0.0:9229"}, 107 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 108 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 109 }, 110 debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, 111 }, 112 { 113 description: "docker-entrypoint (#3821)", 114 containerSpec: v1.Container{}, 115 configuration: debug.ImageConfiguration{ 116 Env: map[string]string{"NODE_VERSION": "10.12"}, 117 Entrypoint: []string{"docker-entrypoint.sh"}, 118 Arguments: []string{"npm run script"}}, 119 result: v1.Container{ 120 Env: []v1.EnvVar{{Name: "NODE_OPTIONS", Value: "--inspect=0.0.0.0:9229"}, {Name: "PATH", Value: "/dbg/nodejs/bin"}}, 121 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 122 }, 123 debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, 124 }, 125 { 126 description: "image environment not copied", 127 containerSpec: v1.Container{Env: []v1.EnvVar{{Name: "OTHER", Value: "VALUE"}}}, 128 configuration: debug.ImageConfiguration{Entrypoint: []string{"node"}, Env: map[string]string{"RANDOM": "VALUE"}}, 129 result: v1.Container{ 130 Command: []string{"node", "--inspect=0.0.0.0:9229"}, 131 Env: []v1.EnvVar{{Name: "OTHER", Value: "VALUE"}, {Name: "PATH", Value: "/dbg/nodejs/bin"}}, 132 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 133 }, 134 debugConfig: types.ContainerDebugConfiguration{Runtime: "nodejs", Ports: map[string]uint32{"devtools": 9229}}, 135 }, 136 } 137 var identity debug.PortAllocator = func(port int32) int32 { 138 return port 139 } 140 for _, test := range tests { 141 testutil.Run(t, test.description, func(t *testutil.T) { 142 adapter := adapter.NewAdapter(&test.containerSpec) 143 config, image, err := debug.NewNodeTransformer().Apply(adapter, test.configuration, identity, nil) 144 adapter.Apply() 145 146 // Apply never fails since there's always the option to set NODE_OPTIONS 147 t.CheckNil(err) 148 t.CheckDeepEqual(test.result, test.containerSpec) 149 t.CheckDeepEqual(test.debugConfig, config) 150 t.CheckDeepEqual("nodejs", image) 151 }) 152 } 153 } 154 155 func TestTransformManifestNodeJS(t *testing.T) { 156 int32p := func(x int32) *int32 { return &x } 157 tests := []struct { 158 description string 159 in runtime.Object 160 transformed bool 161 out runtime.Object 162 }{ 163 { 164 "Pod with no transformable container", 165 &v1.Pod{ 166 Spec: v1.PodSpec{Containers: []v1.Container{{ 167 Name: "test", 168 Command: []string{"echo", "Hello World"}, 169 }}}}, 170 false, 171 &v1.Pod{ 172 Spec: v1.PodSpec{Containers: []v1.Container{{ 173 Name: "test", 174 Command: []string{"echo", "Hello World"}, 175 }}}}, 176 }, 177 { 178 "Pod with NodeJS container", 179 &v1.Pod{ 180 Spec: v1.PodSpec{Containers: []v1.Container{{ 181 Name: "test", 182 Command: []string{"node", "foo.js"}, 183 }}}}, 184 true, 185 &v1.Pod{ 186 ObjectMeta: metav1.ObjectMeta{ 187 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"nodejs","ports":{"devtools":9229}}}`}, 188 }, 189 Spec: v1.PodSpec{ 190 Containers: []v1.Container{{ 191 Name: "test", 192 Command: []string{"node", "--inspect=0.0.0.0:9229", "foo.js"}, 193 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 194 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 195 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 196 }}, 197 InitContainers: []v1.Container{{ 198 Name: "install-nodejs-debug-support", 199 Image: "HELPERS/nodejs", 200 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 201 }}, 202 Volumes: []v1.Volume{{ 203 Name: "debugging-support-files", 204 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 205 }}, 206 }}, 207 }, 208 { 209 "Deployment with NodeJS container", 210 &appsv1.Deployment{ 211 Spec: appsv1.DeploymentSpec{ 212 Replicas: int32p(2), 213 Template: v1.PodTemplateSpec{ 214 Spec: v1.PodSpec{Containers: []v1.Container{{ 215 Name: "test", 216 Command: []string{"node", "foo.js"}, 217 }}}}}}, 218 true, 219 &appsv1.Deployment{ 220 Spec: appsv1.DeploymentSpec{ 221 Replicas: int32p(1), 222 Template: v1.PodTemplateSpec{ 223 ObjectMeta: metav1.ObjectMeta{ 224 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"nodejs","ports":{"devtools":9229}}}`}, 225 }, 226 Spec: v1.PodSpec{ 227 Containers: []v1.Container{{ 228 Name: "test", 229 Command: []string{"node", "--inspect=0.0.0.0:9229", "foo.js"}, 230 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 231 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 232 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 233 }}, 234 InitContainers: []v1.Container{{ 235 Name: "install-nodejs-debug-support", 236 Image: "HELPERS/nodejs", 237 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 238 }}, 239 Volumes: []v1.Volume{{ 240 Name: "debugging-support-files", 241 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 242 }}, 243 }}}}, 244 }, 245 { 246 "ReplicaSet with NodeJS container", 247 &appsv1.ReplicaSet{ 248 Spec: appsv1.ReplicaSetSpec{ 249 Replicas: int32p(2), 250 Template: v1.PodTemplateSpec{ 251 Spec: v1.PodSpec{Containers: []v1.Container{{ 252 Name: "test", 253 Command: []string{"node", "foo.js"}, 254 }}}}}}, 255 true, 256 &appsv1.ReplicaSet{ 257 Spec: appsv1.ReplicaSetSpec{ 258 Replicas: int32p(1), 259 Template: v1.PodTemplateSpec{ 260 ObjectMeta: metav1.ObjectMeta{ 261 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"nodejs","ports":{"devtools":9229}}}`}, 262 }, 263 Spec: v1.PodSpec{ 264 Containers: []v1.Container{{ 265 Name: "test", 266 Command: []string{"node", "--inspect=0.0.0.0:9229", "foo.js"}, 267 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 268 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 269 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 270 }}, 271 InitContainers: []v1.Container{{ 272 Name: "install-nodejs-debug-support", 273 Image: "HELPERS/nodejs", 274 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 275 }}, 276 Volumes: []v1.Volume{{ 277 Name: "debugging-support-files", 278 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 279 }}, 280 }}}}, 281 }, 282 { 283 "StatefulSet with NodeJS container", 284 &appsv1.StatefulSet{ 285 Spec: appsv1.StatefulSetSpec{ 286 Replicas: int32p(2), 287 Template: v1.PodTemplateSpec{ 288 Spec: v1.PodSpec{Containers: []v1.Container{{ 289 Name: "test", 290 Command: []string{"node", "foo.js"}, 291 }}}}}}, 292 true, 293 &appsv1.StatefulSet{ 294 Spec: appsv1.StatefulSetSpec{ 295 Replicas: int32p(1), 296 Template: v1.PodTemplateSpec{ 297 ObjectMeta: metav1.ObjectMeta{ 298 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"nodejs","ports":{"devtools":9229}}}`}, 299 }, 300 Spec: v1.PodSpec{ 301 Containers: []v1.Container{{ 302 Name: "test", 303 Command: []string{"node", "--inspect=0.0.0.0:9229", "foo.js"}, 304 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 305 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 306 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 307 }}, 308 InitContainers: []v1.Container{{ 309 Name: "install-nodejs-debug-support", 310 Image: "HELPERS/nodejs", 311 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 312 }}, 313 Volumes: []v1.Volume{{ 314 Name: "debugging-support-files", 315 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 316 }}, 317 }}}}, 318 }, 319 { 320 "DaemonSet with NodeJS container", 321 &appsv1.DaemonSet{ 322 Spec: appsv1.DaemonSetSpec{ 323 Template: v1.PodTemplateSpec{ 324 Spec: v1.PodSpec{Containers: []v1.Container{{ 325 Name: "test", 326 Command: []string{"node", "foo.js"}, 327 }}}}}}, 328 true, 329 &appsv1.DaemonSet{ 330 Spec: appsv1.DaemonSetSpec{ 331 Template: v1.PodTemplateSpec{ 332 ObjectMeta: metav1.ObjectMeta{ 333 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"nodejs","ports":{"devtools":9229}}}`}, 334 }, 335 Spec: v1.PodSpec{ 336 Containers: []v1.Container{{ 337 Name: "test", 338 Command: []string{"node", "--inspect=0.0.0.0:9229", "foo.js"}, 339 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 340 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 341 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 342 }}, 343 InitContainers: []v1.Container{{ 344 Name: "install-nodejs-debug-support", 345 Image: "HELPERS/nodejs", 346 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 347 }}, 348 Volumes: []v1.Volume{{ 349 Name: "debugging-support-files", 350 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 351 }}, 352 }}}}, 353 }, 354 { 355 "Job with NodeJS container", 356 &batchv1.Job{ 357 Spec: batchv1.JobSpec{ 358 Template: v1.PodTemplateSpec{ 359 Spec: v1.PodSpec{ 360 Containers: []v1.Container{{ 361 Name: "test", 362 Command: []string{"node", "foo.js"}, 363 }}}}}}, 364 true, 365 &batchv1.Job{ 366 Spec: batchv1.JobSpec{ 367 Template: v1.PodTemplateSpec{ 368 ObjectMeta: metav1.ObjectMeta{ 369 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"nodejs","ports":{"devtools":9229}}}`}, 370 }, 371 Spec: v1.PodSpec{ 372 Containers: []v1.Container{{ 373 Name: "test", 374 Command: []string{"node", "--inspect=0.0.0.0:9229", "foo.js"}, 375 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 376 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 377 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 378 }}, 379 InitContainers: []v1.Container{{ 380 Name: "install-nodejs-debug-support", 381 Image: "HELPERS/nodejs", 382 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 383 }}, 384 Volumes: []v1.Volume{{ 385 Name: "debugging-support-files", 386 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 387 }}, 388 }}}}, 389 }, 390 { 391 "ReplicationController with NodeJS container", 392 &v1.ReplicationController{ 393 Spec: v1.ReplicationControllerSpec{ 394 Replicas: int32p(2), 395 Template: &v1.PodTemplateSpec{ 396 Spec: v1.PodSpec{Containers: []v1.Container{ 397 { 398 Name: "test", 399 Command: []string{"node", "foo.js"}, 400 }, 401 }}}}}, 402 true, 403 &v1.ReplicationController{ 404 Spec: v1.ReplicationControllerSpec{ 405 Replicas: int32p(1), 406 Template: &v1.PodTemplateSpec{ 407 ObjectMeta: metav1.ObjectMeta{ 408 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"nodejs","ports":{"devtools":9229}}}`}, 409 }, 410 Spec: v1.PodSpec{ 411 Containers: []v1.Container{{ 412 Name: "test", 413 Command: []string{"node", "--inspect=0.0.0.0:9229", "foo.js"}, 414 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 415 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 416 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 417 }}, 418 InitContainers: []v1.Container{{ 419 Name: "install-nodejs-debug-support", 420 Image: "HELPERS/nodejs", 421 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 422 }}, 423 Volumes: []v1.Volume{{ 424 Name: "debugging-support-files", 425 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 426 }}, 427 }}}}, 428 }, 429 { 430 "PodList with Java and non-Java container", 431 &v1.PodList{ 432 Items: []v1.Pod{ 433 { 434 Spec: v1.PodSpec{Containers: []v1.Container{{ 435 Name: "echo", 436 Command: []string{"echo", "Hello World"}, 437 }}}, 438 }, 439 { 440 Spec: v1.PodSpec{Containers: []v1.Container{{ 441 Name: "test", 442 Command: []string{"node", "foo.js"}, 443 }}}, 444 }, 445 }}, 446 true, 447 &v1.PodList{ 448 Items: []v1.Pod{ 449 { 450 Spec: v1.PodSpec{ 451 Containers: []v1.Container{{ 452 Name: "echo", 453 Command: []string{"echo", "Hello World"}, 454 }}}, 455 }, 456 { 457 ObjectMeta: metav1.ObjectMeta{ 458 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"nodejs","ports":{"devtools":9229}}}`}, 459 }, 460 Spec: v1.PodSpec{ 461 Containers: []v1.Container{{ 462 Name: "test", 463 Command: []string{"node", "--inspect=0.0.0.0:9229", "foo.js"}, 464 Ports: []v1.ContainerPort{{Name: "devtools", ContainerPort: 9229}}, 465 Env: []v1.EnvVar{{Name: "PATH", Value: "/dbg/nodejs/bin"}}, 466 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 467 }}, 468 InitContainers: []v1.Container{{ 469 Name: "install-nodejs-debug-support", 470 Image: "HELPERS/nodejs", 471 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 472 }}, 473 Volumes: []v1.Volume{{ 474 Name: "debugging-support-files", 475 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 476 }}}, 477 }}}, 478 }, 479 } 480 for _, test := range tests { 481 testutil.Run(t, test.description, func(t *testutil.T) { 482 value := test.in.DeepCopyObject() 483 484 retriever := func(image string) (debug.ImageConfiguration, error) { 485 return debug.ImageConfiguration{}, nil 486 } 487 result := transformManifest(value, retriever, "HELPERS") 488 489 t.CheckDeepEqual(test.transformed, result) 490 t.CheckDeepEqual(test.out, value) 491 }) 492 } 493 }