Skip to content

Commit

Permalink
feat(clouddriver): add a new task that checks if the application spec…
Browse files Browse the repository at this point in the history
…ified in the moniker or cluster keys exists in front50 and/or clouddriver (#4788)

* feat(clouddriver): introduce a 'checkIfApplicationExists' task that checks if the application defined in the stage context is known to front50 and/or clouddriver

* also refactor the task configuration properties to make it more manageable as we go ahead and add more task specific config properties.
* also make it easy to reuse common properties like RetryConfig

* feat(clouddriver): add a 'check if application exists' task for server group workflows

* chore(clouddriver): simplify get application name logic in determineHealthProvidersTask

* feat(clouddriver): update server group stages to include 'check if application exists' task

* feat(clouddriver): add a 'check if application exists' task for cluster based workflows

* feat(clouddriver): update cluster based stages to include 'check if application exists' task

* feat(clouddriver): add a 'check if application exists' task for k8s manifest workflows

* feat(clouddriver): update k8s manifest stages to include 'check if application exists' task

* chore(clouddriver): use simpler presence check

* feat(clouddriver): enable audit mode for checking if application exists in front50

---------

Co-authored-by: Apoorv Mahajan <[email protected]>
  • Loading branch information
kirangodishala and apoorv-mahajan authored Oct 1, 2024
1 parent 3e98d0a commit 51a9acf
Show file tree
Hide file tree
Showing 24 changed files with 1,160 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2022 Salesforce.com, 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.config.tasks;

import lombok.Data;

@Data
public class CheckIfApplicationExistsTaskConfig {
// controls whether clouddriver should be queried for an application or not. Defaults to true
boolean checkClouddriver = true;

// controls whether the task should fail or simply log a warning
boolean auditModeEnabled = true;

// front50 specific retry config. This is only applicable when services.front50.enabled: true
private RetryConfig front50Retries = new RetryConfig();

// clouddriver specific retry config. This is only applicable when checkClouddriver: true
private RetryConfig clouddriverRetries = new RetryConfig();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2022 Salesforce.com, 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.config.tasks;

import lombok.Data;

@Data
public class RetryConfig {
// total number of attempts
int maxAttempts = 6;

// time in ms to wait before subsequent retry attempts
long backOffInMs = 5000;

// flag to enable exponential backoff
boolean exponentialBackoffEnabled = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.netflix.spinnaker.orca.clouddriver.ForceCacheRefreshAware
import com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.strategies.AbstractDeployStrategyStage
import com.netflix.spinnaker.orca.clouddriver.tasks.MonitorKatoTask
import com.netflix.spinnaker.orca.clouddriver.tasks.instance.WaitForUpInstancesTask
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.CheckIfApplicationExistsForServerGroupTask
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.CloneServerGroupTask
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.ServerGroupCacheForceRefreshTask
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.AddServerGroupEntityTagsTask
Expand Down Expand Up @@ -78,4 +79,13 @@ class CloneServerGroupStage extends AbstractDeployStrategyStage implements Force

return tasks
}

@Override
protected Map<String, Class> getOptionalPreValidationTasks(){
Map<String, Class> output = [:]
if (isCheckIfApplicationExistsEnabled(dynamicConfigService)) {
output[CheckIfApplicationExistsForServerGroupTask.getTaskName()] = CheckIfApplicationExistsForServerGroupTask
}
return output
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution
import com.netflix.spinnaker.orca.api.pipeline.graph.StageGraphBuilder
import com.netflix.spinnaker.orca.clouddriver.ForceCacheRefreshAware
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.CheckIfApplicationExistsForServerGroupTask
import com.netflix.spinnaker.orca.kato.pipeline.strategy.Strategy

import javax.annotation.Nonnull
Expand All @@ -37,7 +38,6 @@ import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.ServerGroupCache
import com.netflix.spinnaker.orca.clouddriver.utils.MonikerHelper
import com.netflix.spinnaker.orca.api.pipeline.graph.TaskNode
import groovy.util.logging.Slf4j
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component

import static java.util.concurrent.TimeUnit.MINUTES
Expand All @@ -47,20 +47,22 @@ import static java.util.concurrent.TimeUnit.MINUTES
class CreateServerGroupStage extends AbstractDeployStrategyStage implements ForceCacheRefreshAware {
public static final String PIPELINE_CONFIG_TYPE = "createServerGroup"

@Autowired
private FeaturesService featuresService

@Autowired
private RollbackClusterStage rollbackClusterStage

@Autowired
private DestroyServerGroupStage destroyServerGroupStage

@Autowired
private DynamicConfigService dynamicConfigService

CreateServerGroupStage() {
CreateServerGroupStage(
FeaturesService featuresService,
RollbackClusterStage rollbackClusterStage,
DestroyServerGroupStage destroyServerGroupStage,
DynamicConfigService dynamicConfigService
){
super(PIPELINE_CONFIG_TYPE)
this.featuresService = featuresService
this.rollbackClusterStage = rollbackClusterStage
this.destroyServerGroupStage = destroyServerGroupStage
this.dynamicConfigService = dynamicConfigService
}

@Override
Expand Down Expand Up @@ -162,6 +164,15 @@ class CreateServerGroupStage extends AbstractDeployStrategyStage implements Forc
super.onFailureStages(stage, graph)
}

@Override
protected Map<String, Class> getOptionalPreValidationTasks(){
Map<String, Class> output = [:]
if (isCheckIfApplicationExistsEnabled(dynamicConfigService)) {
output[CheckIfApplicationExistsForServerGroupTask.getTaskName()] = CheckIfApplicationExistsForServerGroupTask
}
return output
}

static class StageData {
String application
String account
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.netflix.spinnaker.orca.clouddriver.pipeline.providers.aws.ModifyAwsSc
import com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.support.TargetServerGroupLinearStageSupport
import com.netflix.spinnaker.orca.clouddriver.tasks.DetermineHealthProvidersTask
import com.netflix.spinnaker.orca.clouddriver.tasks.MonitorKatoTask
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.CheckIfApplicationExistsForServerGroupTask
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.ResizeServerGroupTask
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.ServerGroupCacheForceRefreshTask
import com.netflix.spinnaker.orca.clouddriver.tasks.servergroup.WaitForCapacityMatchTask
Expand All @@ -43,6 +44,7 @@ class ResizeServerGroupStage extends TargetServerGroupLinearStageSupport {
@Override
protected void taskGraphInternal(StageExecution stage, TaskNode.Builder builder) {
builder
.withTask(CheckIfApplicationExistsForServerGroupTask.getTaskName(), CheckIfApplicationExistsForServerGroupTask)
.withTask("determineHealthProviders", DetermineHealthProvidersTask)
.withTask("resizeServerGroup", ResizeServerGroupTask)
.withTask("monitorServerGroup", MonitorKatoTask)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.strategies

import com.google.common.base.CaseFormat
import com.netflix.spinnaker.kork.dynamicconfig.DynamicConfigService
import com.netflix.spinnaker.moniker.Moniker
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution
import com.netflix.spinnaker.orca.api.pipeline.graph.StageGraphBuilder
Expand Down Expand Up @@ -67,8 +69,23 @@ abstract class AbstractDeployStrategyStage extends AbstractCloudProviderAwareSta
protected
abstract List<TaskNode.TaskDefinition> basicTasks(StageExecution stage)

/**
* helper method that returns a map of task name to task class that are added to a stage. These
* tasks are added only if the correct configuration property is set.
*
*
* @return map of task name to task class
*/
protected Map<String, Class> getOptionalPreValidationTasks() {
return [:]
}

@Override
void taskGraph(@Nonnull StageExecution stage, @Nonnull TaskNode.Builder builder) {
// add any optional pre-validation tasks at the beginning of the stage
getOptionalPreValidationTasks().each {key, val ->
builder.withTask(key, val)
}

String cloudProvider = getCloudProvider(stage);
if ("cloudrun".equals(cloudProvider)) {
Expand Down Expand Up @@ -193,6 +210,29 @@ abstract class AbstractDeployStrategyStage extends AbstractCloudProviderAwareSta
}
}

/**
* method that checks if the check if application exists task is enabled via
* configuration properties
*
* @param dynamicConfigService config properties
* @return true, if the config is set, false otherwise
*/
boolean isCheckIfApplicationExistsEnabled(DynamicConfigService dynamicConfigService) {
String className = getClass().getSimpleName();

try {
return dynamicConfigService.isEnabled(
String.format(
"stages.%s.check-if-application-exists",
CaseFormat.LOWER_CAMEL.to(
CaseFormat.LOWER_HYPHEN,
Character.toLowerCase(className.charAt(0)).toString() + className.substring(1))),
false)
} catch (Exception ignored) {
return false
}
}

/**
* This nasty method is here because of an unfortunate misstep in pipeline configuration that introduced a nested
* "cluster" key, when in reality we want all of the parameters to be derived from the top level. To preserve
Expand Down
Loading

0 comments on commit 51a9acf

Please sign in to comment.