From 53606c1a3ce850481aff42deb470ed6309f05128 Mon Sep 17 00:00:00 2001 From: David Byron <82477955+dbyron-sf@users.noreply.github.com> Date: Fri, 16 Aug 2024 09:47:04 -0700 Subject: [PATCH] fix(kubernetes): teach deployManifest stages to handle label selectors with generateName (#6265) This fixes: java.lang.NullPointerException: Cannot invoke "com.netflix.spinnaker.clouddriver.kubernetes.description.manifest.KubernetesManifest.getNamespace()" because "manifest" is null at com.netflix.spinnaker.clouddriver.kubernetes.op.OperationResult.addManifest(OperationResult.java:46) at com.netflix.spinnaker.clouddriver.kubernetes.op.handler.CanDeploy.deploy(CanDeploy.java:44) at com.netflix.spinnaker.clouddriver.kubernetes.op.manifest.KubernetesDeployManifestOperation.lambda$operate$3(KubernetesDeployManifestOperation.java:174) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at com.netflix.spinnaker.clouddriver.kubernetes.op.manifest.KubernetesDeployManifestOperation.operate(KubernetesDeployManifestOperation.java:162) at com.netflix.spinnaker.clouddriver.kubernetes.op.manifest.KubernetesDeployManifestOperation.operate(KubernetesDeployManifestOperation.java:48) at com.netflix.spinnaker.clouddriver.orchestration.AtomicOperation$operate.call(Unknown Source) at com.netflix.spinnaker.clouddriver.orchestration.DefaultOrchestrationProcessor$_process_closure1$_closure2.doCall(DefaultOrchestrationProcessor.groovy:120) at com.netflix.spinnaker.clouddriver.orchestration.DefaultOrchestrationProcessor$_process_closure1$_closure2.doCall(DefaultOrchestrationProcessor.groovy) at jdk.internal.reflect.GeneratedMethodAccessor373.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:274) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1035) at groovy.lang.Closure.call(Closure.java:412) at groovy.lang.Closure.call(Closure.java:406) at com.netflix.spinnaker.clouddriver.metrics.TimedCallable$CallableWrapper.call(TimedCallable.java:81) at com.netflix.spinnaker.clouddriver.metrics.TimedCallable.call(TimedCallable.java:45) at java_util_concurrent_Callable$call.call(Unknown Source) at com.netflix.spinnaker.clouddriver.orchestration.DefaultOrchestrationProcessor$_process_closure1.doCall(DefaultOrchestrationProcessor.groovy:119) at com.netflix.spinnaker.clouddriver.orchestration.DefaultOrchestrationProcessor$_process_closure1.doCall(DefaultOrchestrationProcessor.groovy) at jdk.internal.reflect.GeneratedMethodAccessor367.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:274) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1035) at groovy.lang.Closure.call(Closure.java:412) at groovy.lang.Closure.call(Closure.java:406) at com.netflix.spinnaker.security.AuthenticatedRequest.lambda$wrapCallableForPrincipal$0(AuthenticatedRequest.java:272) at com.netflix.spinnaker.clouddriver.metrics.TimedCallable.call(TimedCallable.java:45) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:840) --- .../kubernetes/op/handler/CanDeploy.java | 6 ++++- .../kubernetes/op/handler/CanDeployTest.java | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/clouddriver-kubernetes/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/op/handler/CanDeploy.java b/clouddriver-kubernetes/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/op/handler/CanDeploy.java index 5f868b1e0f..2eab5894b0 100644 --- a/clouddriver-kubernetes/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/op/handler/CanDeploy.java +++ b/clouddriver-kubernetes/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/op/handler/CanDeploy.java @@ -41,7 +41,11 @@ default OperationResult deploy( // require looking up a manifest by name, which will fail. if (manifest.hasGenerateName()) { KubernetesManifest result = credentials.create(manifest, task, opName, labelSelectors); - return new OperationResult().addManifest(result); + OperationResult operationResult = new OperationResult(); + if (result != null) { + operationResult.addManifest(result); + } + return operationResult; } KubernetesManifest deployedManifest; diff --git a/clouddriver-kubernetes/src/test/java/com/netflix/spinnaker/clouddriver/kubernetes/op/handler/CanDeployTest.java b/clouddriver-kubernetes/src/test/java/com/netflix/spinnaker/clouddriver/kubernetes/op/handler/CanDeployTest.java index b4255fb7a3..80344494b7 100644 --- a/clouddriver-kubernetes/src/test/java/com/netflix/spinnaker/clouddriver/kubernetes/op/handler/CanDeployTest.java +++ b/clouddriver-kubernetes/src/test/java/com/netflix/spinnaker/clouddriver/kubernetes/op/handler/CanDeployTest.java @@ -267,4 +267,30 @@ void nullManifest() { verify(credentials).deploy(manifest, task, OP_NAME, selectorList); assertThat(result.getManifests()).isEmpty(); } + + @Test + void nullManifestWithGenerateName() { + KubernetesCredentials credentials = mock(KubernetesCredentials.class); + KubernetesManifest manifest = + ManifestFetcher.getManifest("candeploy/deployment-generate-name.yml"); + assertThat(manifest.getGenerateName()).isNotBlank(); + + KubernetesSelectorList selectorList = new KubernetesSelectorList(); + when(credentials.create(manifest, task, OP_NAME, selectorList)).thenReturn(null); + + // DeployStrategy.APPLY and ServerSideApplyStrategy.DEFAULT are arbitrary + // too since they're ignored for manifests with generateName. + OperationResult result = + handler.deploy( + credentials, + manifest, + DeployStrategy.APPLY, + ServerSideApplyStrategy.DEFAULT, + task, + OP_NAME, + selectorList); + + verify(credentials).create(manifest, task, OP_NAME, selectorList); + assertThat(result.getManifests()).isEmpty(); + } }