Skip to content

Commit

Permalink
feat(provider/google): Added cloudrun manifest functionality in orca. (
Browse files Browse the repository at this point in the history
…#4396)

* feat(provider/google): Added cloudrun manifest functionality in orca.

* Added required classes in imports as per the comments suggested.

* Added required classes in imports as per the comments suggested.
  • Loading branch information
sanopsmx committed Aug 2, 2023
1 parent 45de5bc commit d64c491
Show file tree
Hide file tree
Showing 5 changed files with 536 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* Copyright 2022 OpsMx, 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.orca.clouddriver.pipeline.manifest;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Collections.emptyMap;

import com.google.common.collect.ImmutableList;
import com.netflix.spinnaker.kork.expressions.ExpressionEvaluationSummary;
import com.netflix.spinnaker.orca.api.pipeline.graph.StageGraphBuilder;
import com.netflix.spinnaker.orca.api.pipeline.graph.TaskNode;
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution;
import com.netflix.spinnaker.orca.clouddriver.OortService;
import com.netflix.spinnaker.orca.clouddriver.model.ManifestCoordinates;
import com.netflix.spinnaker.orca.clouddriver.tasks.MonitorKatoTask;
import com.netflix.spinnaker.orca.clouddriver.tasks.artifacts.CleanupArtifactsTask;
import com.netflix.spinnaker.orca.clouddriver.tasks.manifest.*;
import com.netflix.spinnaker.orca.clouddriver.tasks.manifest.DeployManifestContext.TrafficManagement;
import com.netflix.spinnaker.orca.pipeline.ExpressionAwareStageDefinitionBuilder;
import com.netflix.spinnaker.orca.pipeline.tasks.artifacts.BindProducedArtifactsTask;
import com.netflix.spinnaker.orca.pipeline.util.ContextParameterProcessor;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DeployCloudrunManifestStage extends ExpressionAwareStageDefinitionBuilder {

public static final String PIPELINE_CONFIG_TYPE = "deployCloudrunManifest";

private final OortService oortService;

@Autowired
public DeployCloudrunManifestStage(OortService oortService) {
this.oortService = oortService;
}

@Override
public void taskGraph(@Nonnull StageExecution stage, @Nonnull TaskNode.Builder builder) {
builder
.withTask(ResolveDeploySourceManifestTask.TASK_NAME, ResolveDeploySourceManifestTask.class)
.withTask(DeployCloudrunManifestTask.TASK_NAME, DeployCloudrunManifestTask.class)
.withTask("monitorDeploy", MonitorKatoTask.class)
.withTask(PromoteManifestKatoOutputsTask.TASK_NAME, PromoteManifestKatoOutputsTask.class)
.withTask(WaitForManifestStableTask.TASK_NAME, WaitForManifestStableTask.class)
.withTask(CleanupArtifactsTask.TASK_NAME, CleanupArtifactsTask.class)
.withTask("monitorCleanup", MonitorKatoTask.class)
.withTask(PromoteManifestKatoOutputsTask.TASK_NAME, PromoteManifestKatoOutputsTask.class)
.withTask(BindProducedArtifactsTask.TASK_NAME, BindProducedArtifactsTask.class);
}

public void afterStages(@Nonnull StageExecution stage, @Nonnull StageGraphBuilder graph) {
TrafficManagement trafficManagement =
stage.mapTo(DeployManifestContext.class).getTrafficManagement();
if (trafficManagement.isEnabled()) {
switch (trafficManagement.getOptions().getStrategy()) {
case RED_BLACK:
disableOldManifests(stage.getContext(), graph);
break;
case HIGHLANDER:
disableOldManifests(stage.getContext(), graph);
deleteOldManifests(stage.getContext(), graph);
break;
case NONE:
// do nothing
}
}
if (stage.getContext().getOrDefault("noOutput", "false").toString().equals("true")) {
stage.setOutputs(emptyMap());
}
}

private void disableOldManifests(Map<String, Object> parentContext, StageGraphBuilder graph) {
addStagesForOldManifests(parentContext, graph, DisableManifestStage.PIPELINE_CONFIG_TYPE);
}

private void deleteOldManifests(Map<String, Object> parentContext, StageGraphBuilder graph) {
addStagesForOldManifests(parentContext, graph, DeleteManifestStage.PIPELINE_CONFIG_TYPE);
}

private void addStagesForOldManifests(
Map<String, Object> parentContext, StageGraphBuilder graph, String stageType) {
List<Map<String, ?>> deployedManifests = getNewManifests(parentContext);
String account = (String) parentContext.get("account");
Map manifestMoniker = (Map) parentContext.get("moniker");
String application = (String) manifestMoniker.get("app");

deployedManifests.forEach(
manifest -> {
Map manifestMetadata = (Map) manifest.get("metadata");
String manifestName =
String.format("replicaSet %s", (String) manifestMetadata.get("name"));
String namespace = (String) manifestMetadata.get("namespace");
Map annotations = (Map) manifestMetadata.get("annotations");
String clusterName = (String) annotations.get("moniker.spinnaker.io/cluster");
String cloudProvider = "kubernetes";

ImmutableList<String> previousManifestNames =
getOldManifestNames(application, account, clusterName, namespace, manifestName);
previousManifestNames.forEach(
name -> {
graph.append(
(stage) -> {
stage.setType(stageType);
Map<String, Object> context = stage.getContext();
context.put("account", account);
context.put("app", application);
context.put("cloudProvider", cloudProvider);
context.put("manifestName", name);
context.put("location", namespace);
});
});
});
}

private List<Map<String, ?>> getNewManifests(Map<String, Object> parentContext) {
List<Map<String, ?>> manifests = (List<Map<String, ?>>) parentContext.get("outputs.manifests");
return manifests.stream()
.filter(manifest -> manifest.get("kind").equals("ReplicaSet"))
.collect(Collectors.toList());
}

private ImmutableList<String> getOldManifestNames(
String application,
String account,
String clusterName,
String namespace,
String newManifestName) {
return oortService
.getClusterManifests(account, namespace, "replicaSet", application, clusterName)
.stream()
.filter(m -> !m.getFullResourceName().equals(newManifestName))
.map(ManifestCoordinates::getFullResourceName)
.collect(toImmutableList());
}

@Override
public boolean processExpressions(
@Nonnull StageExecution stage,
@Nonnull ContextParameterProcessor contextParameterProcessor,
@Nonnull ExpressionEvaluationSummary summary) {
DeployManifestContext context = stage.mapTo(DeployManifestContext.class);
if (context.isSkipExpressionEvaluation()) {
processDefaultEntries(
stage, contextParameterProcessor, summary, Collections.singletonList("manifests"));
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2022 OpsMx, 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.orca.clouddriver.pipeline.providers.cloudrun;

import com.netflix.spinnaker.orca.api.pipeline.graph.StageDefinitionBuilder;
import com.netflix.spinnaker.orca.api.pipeline.graph.TaskNode;
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution;
import com.netflix.spinnaker.orca.clouddriver.tasks.MonitorKatoTask;
import com.netflix.spinnaker.orca.clouddriver.tasks.loadbalancer.UpsertLoadBalancerResultObjectExtrapolationTask;
import com.netflix.spinnaker.orca.clouddriver.tasks.providers.cloudrun.UpsertCloudrunLoadBalancersTask;
import javax.annotation.Nonnull;
import org.springframework.stereotype.Component;

@Component
public class UpsertCloudrunLoadBalancersStage implements StageDefinitionBuilder {
@Override
public void taskGraph(@Nonnull StageExecution stage, @Nonnull TaskNode.Builder builder) {
builder
.withTask("upsertLoadBalancers", UpsertCloudrunLoadBalancersTask.class)
.withTask("monitorUpsert", MonitorKatoTask.class)
.withTask("extrapolateUpsertResult", UpsertLoadBalancerResultObjectExtrapolationTask.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2022 OpsMx, 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.orca.clouddriver.tasks.manifest;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.netflix.spinnaker.kork.annotations.NonnullByDefault;
import com.netflix.spinnaker.orca.api.pipeline.Task;
import com.netflix.spinnaker.orca.api.pipeline.TaskResult;
import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus;
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution;
import com.netflix.spinnaker.orca.clouddriver.KatoService;
import com.netflix.spinnaker.orca.clouddriver.model.TaskId;
import com.netflix.spinnaker.orca.clouddriver.utils.CloudProviderAware;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@NonnullByDefault
public final class DeployCloudrunManifestTask implements CloudProviderAware, Task {

public static final String TASK_NAME = "deployCloudrunManifest";

private final KatoService katoService;

@Autowired
public DeployCloudrunManifestTask(KatoService katoService) {
this.katoService = katoService;
}

@Nonnull
@Override
public TaskResult execute(@Nonnull StageExecution stage) {
ImmutableMap<String, Map> operation = getOperation(stage);
TaskId taskId = executeOperation(stage, operation);
ImmutableMap<String, Object> outputs = getOutputs(stage, taskId);
return TaskResult.builder(ExecutionStatus.SUCCEEDED).context(outputs).build();
}

private ImmutableMap<String, Map> getOperation(StageExecution stage) {
DeployManifestContext context = stage.mapTo(DeployManifestContext.class);

Map<String, Object> task = new HashMap<>(stage.getContext());

task.put("source", "text");
if (context.getTrafficManagement().isEnabled()) {
task.put("services", context.getTrafficManagement().getOptions().getServices());
task.put("enableTraffic", context.getTrafficManagement().getOptions().isEnableTraffic());
task.put("strategy", context.getTrafficManagement().getOptions().getStrategy().name());
} else {
// For backwards compatibility, traffic is always enabled to new server groups when the new
// traffic management
// features are not enabled.
task.put("enableTraffic", true);
}

return ImmutableMap.of(TASK_NAME, task);
}

private TaskId executeOperation(StageExecution stage, ImmutableMap<String, Map> operation) {
return katoService.requestOperations(getCloudProvider(stage), ImmutableList.of(operation));
}

private ImmutableMap<String, Object> getOutputs(StageExecution stage, TaskId taskId) {
return new ImmutableMap.Builder<String, Object>()
.put("kato.result.expected", true)
.put("kato.last.task.id", taskId)
.put("deploy.account.name", Objects.requireNonNull(getCredentials(stage)))
.build();
}
}
Loading

0 comments on commit d64c491

Please sign in to comment.