diff --git a/clouddriver-core/src/main/groovy/com/netflix/spinnaker/clouddriver/orchestration/AtomicOperations.java b/clouddriver-core/src/main/groovy/com/netflix/spinnaker/clouddriver/orchestration/AtomicOperations.java index 2dd4f4896c6..73d609a69a7 100644 --- a/clouddriver-core/src/main/groovy/com/netflix/spinnaker/clouddriver/orchestration/AtomicOperations.java +++ b/clouddriver-core/src/main/groovy/com/netflix/spinnaker/clouddriver/orchestration/AtomicOperations.java @@ -87,6 +87,7 @@ public final class AtomicOperations { public static final String PAUSE_ROLLOUT_MANIFEST = "pauseRolloutManifest"; public static final String RESUME_ROLLOUT_MANIFEST = "resumeRolloutManifest"; public static final String UNDO_ROLLOUT_MANIFEST = "undoRolloutManifest"; + public static final String ROLLING_RESTART_MANIFEST = "rollingRestartManifest"; public static final String DISABLE_MANIFEST = "disableManifest"; public static final String ENABLE_MANIFEST = "enableManifest"; diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/converter/manifest/KubernetesRollingRestartManifestConverter.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/converter/manifest/KubernetesRollingRestartManifestConverter.java new file mode 100644 index 00000000000..feb2ac1ee11 --- /dev/null +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/converter/manifest/KubernetesRollingRestartManifestConverter.java @@ -0,0 +1,50 @@ +/* + * Copyright 2019 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.clouddriver.kubernetes.v2.converter.manifest; + +import static com.netflix.spinnaker.clouddriver.orchestration.AtomicOperations.ROLLING_RESTART_MANIFEST; + +import com.netflix.spinnaker.clouddriver.kubernetes.KubernetesOperation; +import com.netflix.spinnaker.clouddriver.kubernetes.deploy.converters.KubernetesAtomicOperationConverterHelper; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesRollingRestartManifestDescription; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.op.manifest.KubernetesRollingRestartManifestOperation; +import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperation; +import com.netflix.spinnaker.clouddriver.security.AbstractAtomicOperationsCredentialsSupport; +import com.netflix.spinnaker.clouddriver.security.ProviderVersion; +import java.util.Map; +import org.springframework.stereotype.Component; + +@KubernetesOperation(ROLLING_RESTART_MANIFEST) +@Component +public class KubernetesRollingRestartManifestConverter + extends AbstractAtomicOperationsCredentialsSupport { + @Override + public AtomicOperation convertOperation(Map input) { + return new KubernetesRollingRestartManifestOperation(convertDescription(input)); + } + + @Override + public KubernetesRollingRestartManifestDescription convertDescription(Map input) { + return KubernetesAtomicOperationConverterHelper.convertDescription( + input, this, KubernetesRollingRestartManifestDescription.class); + } + + @Override + public boolean acceptsVersion(ProviderVersion version) { + return version == ProviderVersion.v2; + } +} diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/description/manifest/KubernetesRollingRestartManifestDescription.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/description/manifest/KubernetesRollingRestartManifestDescription.java new file mode 100644 index 00000000000..4352a49056f --- /dev/null +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/description/manifest/KubernetesRollingRestartManifestDescription.java @@ -0,0 +1,25 @@ +/* + * Copyright 2019 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class KubernetesRollingRestartManifestDescription + extends KubernetesManifestOperationDescription {} diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/CanRollingRestart.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/CanRollingRestart.java new file mode 100644 index 00000000000..a0893529fdc --- /dev/null +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/CanRollingRestart.java @@ -0,0 +1,28 @@ +/* + * Copyright 2019 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler; + +import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesKind; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials; + +public interface CanRollingRestart { + KubernetesKind kind(); + + default void rollingRestart(KubernetesV2Credentials credentials, String namespace, String name) { + credentials.rollingRestart(kind(), namespace, name); + } +} diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDaemonSetHandler.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDaemonSetHandler.java index 516b6cbedb2..20a45e2d598 100644 --- a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDaemonSetHandler.java +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDaemonSetHandler.java @@ -38,7 +38,12 @@ @Component public class KubernetesDaemonSetHandler extends KubernetesHandler - implements CanResize, CanPauseRollout, CanResumeRollout, CanUndoRollout, ServerGroupHandler { + implements CanResize, + CanPauseRollout, + CanResumeRollout, + CanUndoRollout, + CanRollingRestart, + ServerGroupHandler { @Nonnull @Override diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDeploymentHandler.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDeploymentHandler.java index 03cb8d1ebde..205810f38ec 100644 --- a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDeploymentHandler.java +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDeploymentHandler.java @@ -47,6 +47,7 @@ public class KubernetesDeploymentHandler extends KubernetesHandler CanPauseRollout, CanResumeRollout, CanUndoRollout, + CanRollingRestart, ServerGroupManagerHandler { private static final ImmutableSet SUPPORTED_API_VERSIONS = diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesStatefulSetHandler.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesStatefulSetHandler.java index 92d9a41693b..688eb4f972d 100644 --- a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesStatefulSetHandler.java +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesStatefulSetHandler.java @@ -52,6 +52,7 @@ public class KubernetesStatefulSetHandler extends KubernetesHandler CanPauseRollout, CanResumeRollout, CanUndoRollout, + CanRollingRestart, ServerGroupHandler { @Nonnull @Override diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/job/KubectlJobExecutor.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/job/KubectlJobExecutor.java index 4fee749bbad..64375e1824c 100644 --- a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/job/KubectlJobExecutor.java +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/job/KubectlJobExecutor.java @@ -346,6 +346,31 @@ public Void resumeRollout( return null; } + public Void rollingRestart( + KubernetesV2Credentials credentials, KubernetesKind kind, String namespace, String name) { + List command = kubectlNamespacedAuthPrefix(credentials, namespace); + + command.add("rollout"); + command.add("restart"); + command.add(kind.toString() + "/" + name); + + JobResult status = jobExecutor.runJob(new JobRequest(command)); + + if (status.getResult() != JobResult.Result.SUCCESS) { + throw new KubectlException( + "Failed to complete rolling restart of " + + kind + + "/" + + name + + " from " + + namespace + + ": " + + status.getError()); + } + + return null; + } + public KubernetesManifest get( KubernetesV2Credentials credentials, KubernetesKind kind, String namespace, String name) { List command = diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/manifest/KubernetesRollingRestartManifestOperation.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/manifest/KubernetesRollingRestartManifestOperation.java new file mode 100644 index 00000000000..56b5a95b035 --- /dev/null +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/manifest/KubernetesRollingRestartManifestOperation.java @@ -0,0 +1,68 @@ +/* + * Copyright 2019 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.clouddriver.kubernetes.v2.op.manifest; + +import com.netflix.spinnaker.clouddriver.data.task.Task; +import com.netflix.spinnaker.clouddriver.data.task.TaskRepository; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.KubernetesCoordinates; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.KubernetesResourceProperties; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesRollingRestartManifestDescription; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.CanRollingRestart; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.security.KubernetesV2Credentials; +import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperation; +import java.util.List; + +public class KubernetesRollingRestartManifestOperation implements AtomicOperation { + private final KubernetesRollingRestartManifestDescription description; + private final KubernetesV2Credentials credentials; + private static final String OP_NAME = "ROLLING_RESTART_KUBERNETES_MANIFEST"; + + public KubernetesRollingRestartManifestOperation( + KubernetesRollingRestartManifestDescription description) { + this.description = description; + this.credentials = (KubernetesV2Credentials) description.getCredentials().getCredentials(); + } + + private static Task getTask() { + return TaskRepository.threadLocalTask.get(); + } + + @Override + public Void operate(List priorOutputs) { + getTask().updateStatus(OP_NAME, "Starting rolling restart operation..."); + KubernetesCoordinates coordinates = description.getPointCoordinates(); + + getTask().updateStatus(OP_NAME, "Looking up resource properties..."); + KubernetesResourceProperties properties = + credentials.getResourcePropertyRegistry().get(coordinates.getKind()); + KubernetesHandler deployer = properties.getHandler(); + + if (!(deployer instanceof CanRollingRestart)) { + throw new IllegalArgumentException( + "Resource with " + coordinates + " does not support rolling restart"); + } + + CanRollingRestart canRollingRestart = (CanRollingRestart) deployer; + + getTask().updateStatus(OP_NAME, "Calling rolling restart operation..."); + canRollingRestart.rollingRestart( + credentials, coordinates.getNamespace(), coordinates.getName()); + + return null; + } +} diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/security/KubernetesV2Credentials.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/security/KubernetesV2Credentials.java index 0cd73f5efae..08d21e467e3 100644 --- a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/security/KubernetesV2Credentials.java +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/security/KubernetesV2Credentials.java @@ -531,6 +531,14 @@ public void resumeRollout(KubernetesKind kind, String namespace, String name) { () -> jobExecutor.resumeRollout(this, kind, namespace, name)); } + public void rollingRestart(KubernetesKind kind, String namespace, String name) { + runAndRecordMetrics( + "rollingRestart", + kind, + namespace, + () -> jobExecutor.rollingRestart(this, kind, namespace, name)); + } + public void patch( KubernetesKind kind, String namespace, diff --git a/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/validator/manifest/KubernetesRollingRestartManifestValidator.java b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/validator/manifest/KubernetesRollingRestartManifestValidator.java new file mode 100644 index 00000000000..acec60d1572 --- /dev/null +++ b/clouddriver-kubernetes-v2/src/main/java/com/netflix/spinnaker/clouddriver/kubernetes/v2/validator/manifest/KubernetesRollingRestartManifestValidator.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.clouddriver.kubernetes.v2.validator.manifest; + +import static com.netflix.spinnaker.clouddriver.orchestration.AtomicOperations.ROLLING_RESTART_MANIFEST; + +import com.netflix.spinnaker.clouddriver.deploy.DescriptionValidator; +import com.netflix.spinnaker.clouddriver.kubernetes.KubernetesOperation; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesRollingRestartManifestDescription; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.validator.KubernetesValidationUtil; +import com.netflix.spinnaker.clouddriver.security.AccountCredentialsProvider; +import com.netflix.spinnaker.clouddriver.security.ProviderVersion; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; + +@KubernetesOperation(ROLLING_RESTART_MANIFEST) +@Component +public class KubernetesRollingRestartManifestValidator + extends DescriptionValidator { + private final AccountCredentialsProvider provider; + + @Autowired + public KubernetesRollingRestartManifestValidator(AccountCredentialsProvider provider) { + this.provider = provider; + } + + @Override + public void validate( + List priorDescriptions, + KubernetesRollingRestartManifestDescription description, + Errors errors) { + KubernetesValidationUtil util = + new KubernetesValidationUtil("rollingRestartKubernetesManifest", errors); + if (!util.validateV2Credentials( + provider, + description.getAccount(), + description.getPointCoordinates().getKind(), + description.getPointCoordinates().getNamespace())) { + return; + } + } + + @Override + public boolean acceptsVersion(ProviderVersion version) { + return version == ProviderVersion.v2; + } +}