Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: CD Trigger Plugin #4810

Merged
merged 11 commits into from
May 1, 2024
Merged
5 changes: 5 additions & 0 deletions scripts/sql/238_devtron_CD_trigger_v1.0.0.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DELETE FROM plugin_step_variable WHERE plugin_step_id =(SELECT ps.id FROM plugin_metadata p inner JOIN plugin_step ps on ps.plugin_id=p.id WHERE p.name='Devtron CD Trigger v1.0.0' and ps."index"=1 and ps.deleted=false);
DELETE FROM plugin_step WHERE plugin_id=(SELECT id FROM plugin_metadata WHERE name='Devtron CD Trigger v1.0.0');
DELETE FROM pipeline_stage_step_variable where pipeline_stage_step_id in (select id from pipeline_stage_step where name ='Devtron CD Trigger v1.0.0');
DELETE from pipeline_stage_step where name ='Devtron CD Trigger v1.0.0';
DELETE FROM plugin_metadata WHERE name ='Devtron CD Trigger v1.0.0';
274 changes: 274 additions & 0 deletions scripts/sql/238_devtron_CD_trigger_v1.0.0.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
INSERT INTO plugin_metadata (id,name,description,type,icon,deleted,created_on,created_by,updated_on,updated_by)
VALUES (nextval('id_seq_plugin_metadata'),'Devtron CD Trigger v1.0.0', 'The plugin enables users to trigger pre/post/deployment of application. It helps users deploy the application that contains dependencies for their current application.','PRESET','https://raw.githubusercontent.com/devtron-labs/devtron/main/assets/devtron-logo-plugin.png',false,'now()',1,'now()',1);


INSERT INTO plugin_stage_mapping (id,plugin_id,stage_type,created_on,created_by,updated_on,updated_by)VALUES (nextval('id_seq_plugin_stage_mapping'),
(SELECT id from plugin_metadata where name='Devtron CD Trigger v1.0.0'), 0,'now()',1,'now()',1);


INSERT INTO "plugin_pipeline_script" ("id", "script","type","deleted","created_on", "created_by", "updated_on", "updated_by")
VALUES ( nextval('id_seq_plugin_pipeline_script'),
E'#!/bin/bash
pipeline_type=$(echo $CI_CD_EVENT | jq -r \'.type\')
if [[ "$pipeline_type" == "CI" ]]; then
triggeredFromAppName=$(echo $CI_CD_EVENT | jq \'.commonWorkflowRequest.appName\')
triggeredFromPipelineName=$(echo $CI_CD_EVENT | jq \'.commonWorkflowRequest.pipelineName\')
elif [[ "$pipeline_type" == "CD" ]]; then
triggeredFromAppName=$(echo $CI_CD_EVENT | jq \'.commonWorkflowRequest.extraEnvironmentVariables.APP_NAME\')
triggeredFromPipelineName=$(echo $CI_CD_EVENT | jq \'.commonWorkflowRequest.Pipeline.Name\')
fi
# Convert CD Workflow Type to uppercase to make it case-insensitive
TargetTriggerStage=$(echo "$TargetTriggerStage" | tr \'[:lower:]\' \'[:upper:]\')

# Set default value for TargetTriggerStage to DEPLOY if not provided or invalid
case $TargetTriggerStage in
"PRE")
;;
"POST")
;;
"DEPLOY")
;;
"")
TargetTriggerStage="DEPLOY" # Set to DEPLOY if no input provided
;;
*)
echo "Error: Invalid CD Workflow Type. Please enter PRE/DEPLOY/POST. Exiting..."
exit 1
;;
esac

# Set default value for StatusTimeOutSeconds to 0 if not provided or not an integer
if ! [[ "$StatusTimeOutSeconds" =~ ^[0-9]+$ ]]; then
StatusTimeOutSeconds=0
fi

DevtronEndpoint=$(echo "$DevtronEndpoint" | sed \'s:/*$::\')

# Determine sleep interval based on StatusTimeOutSeconds
if [ "$StatusTimeOutSeconds" -lt "60" ]; then
sleepInterval=$(($StatusTimeOutSeconds / 2))
else
sleepInterval=30
fi

verify(){
local response=$(curl -s -H "token: $DevtronApiToken" "$DevtronEndpoint/orchestrator/devtron/auth/verify")
echo $response
exit 1
}
verify_response=$(verify)
verify_status=$( echo "$verify_response" | jq \'.code\')
# echo $verify_status
if [[ "$verify_status" == "401" ]]; then
echo "Enter the valid DevtronApiToken. Exiting..."
exit 1
elif [[ -z "$verify_status" ]]; then
echo "Enter the valid DevtronEndpoint. Exiting..."
exit 1
fi

fetch_app_id() {
# Check if DevtronApp is numeric, if yes, use it directly as App ID
if [[ "$DevtronApp" =~ ^[0-9]+$ ]]; then
echo "$DevtronApp"
else
local api_response=$(curl -s -H "token: $DevtronApiToken" "$DevtronEndpoint/orchestrator/app/autocomplete")
local app_id=$(echo "$api_response" | jq -r --arg app_name "$DevtronApp" \'.result[] | select(.name == $app_name) | .id\')

if [[ -z "$app_id" || "$app_id" == "null" ]]; then
echo "Error: Application \'$DevtronApp\' not found. Please verify the DevtronApp."
exit 1
fi
echo "$app_id"
fi
}

fetch_env_id() {
# Check if DevtronEnv is numeric, if yes, use it directly as Env ID
if [[ "$DevtronEnv" =~ ^[0-9]+$ ]]; then
echo "$DevtronEnv"
else
local api_response=$(curl -s -H "token: $DevtronApiToken" "$DevtronEndpoint/orchestrator/env/autocomplete")
local env_id=$(echo "$api_response" | jq -r --arg env_name "$DevtronEnv" \'.result[] | select(.environment_name == $env_name) | .id\')

if [[ -z "$env_id" || "$env_id" == "null" ]]; then
echo "Error: Environment \'$DevtronEnv\' not found. Please verify the DevtronEnv."
exit 1
fi
echo "$env_id"
fi
}

fetch_pipeline_id() {
local app_id=$1
local env_id=$2
local api_response=$(curl -s -H "token: $DevtronApiToken" "$DevtronEndpoint/orchestrator/app/app-wf/view/$app_id")
local pipeline_id=$(echo "$api_response" | jq -r --arg env_id "$env_id" \'.result.cdConfig.pipelines[] | select(.environmentId == ($env_id | tonumber)) | .id\')

if [[ -z "$pipeline_id" || "$pipeline_id" == "null" ]]; then
echo "Error: Pipeline not found for the provided Environment. Please verify the Environment ID."
echo "Environment ID: $env_id"
echo "API Response: $api_response"
exit 1
fi
echo "$pipeline_id"
}

fetch_ci_artifact_id() {
local pipeline_id=$1
local apiUrl="$DevtronEndpoint/orchestrator/app/cd-pipeline/$pipeline_id/material?offset=0&size=20&stage=$TargetTriggerStage"
local apiResponse=$(curl -s -H "token: $DevtronApiToken" "$apiUrl")

local ciArtifactId=""
if [[ -n "$GitCommitHash" ]]; then
ciArtifactId=$(echo "$apiResponse" | jq -r --arg hash "$GitCommitHash" \'.result.ci_artifacts[] | select(.material_info[].revision == $hash) | .id\' | head -n 1)
if [[ -z "$ciArtifactId" || "$ciArtifactId" == "null" || "$ciArtifactId" == "" ]]; then
echo "Error: CI Artifact ID for the provided commit hash \'$GitCommitHash\' not found. Please verify the commit hash."
exit 1
fi
else
ciArtifactId=$(echo "$apiResponse" | jq -r \'.result.ci_artifacts[0].id\')
if [[ -z "$ciArtifactId" || "$ciArtifactId" == "null" ]]; then
echo "Error: CI Artifact ID not found."
exit 1
fi
fi

echo "$ciArtifactId"
}

# Fetch the app ID. Exit the script if the app name is incorrect.
app_id=$(fetch_app_id)
if [ $? -ne 0 ]; then
echo "Error: Application \'$DevtronApp\' not found. Enter the correct App Name. Exiting...."
exit 1
fi

# Fetch the env ID. Exit the script if the environment name or ID is incorrect.
env_id=$(fetch_env_id)
if [ $? -ne 0 ]; then
echo "Error: Environment \'$DevtronEnv\' not found. Enter the correct Environment Name. Exiting..."
exit 1
fi

# Fetch the pipeline ID using the env ID. Exit the script if the environment is incorrect.
pipeline_id=$(fetch_pipeline_id "$app_id" "$env_id")
if [ $? -ne 0 ]; then
echo "Verify your App Name/ID and Env Name/ID. Exiting..."
exit 1
fi

# Fetch the CI Artifact ID based on the commit hash.
ciArtifactId=$(fetch_ci_artifact_id "$pipeline_id")
if [ $? -ne 0 ]; then
echo "Enter the correct GitCommitHash. Exiting..."
exit 1
fi

trigger_cd_pipeline() {
local pipeline_id=$1
local app_id=$2
local ciArtifactId=$3
local jsonPayload=$(jq -n \\
--arg pipelineId "$pipeline_id" \\
--arg appId "$app_id" \\
--arg ciArtifactId "$ciArtifactId" \\
--arg TargetTriggerStage "$TargetTriggerStage" \\
--arg deploymentWithConfig "LAST_SAVED_CONFIG" \\
--arg triggeredFromAppName "$triggeredFromAppName" \\
--arg triggeredFromPipelineName "$triggeredFromPipelineName" \\
\'{
pipelineId: ($pipelineId | tonumber),
appId: ($appId | tonumber),
ciArtifactId: ($ciArtifactId | tonumber),
cdWorkflowType: $TargetTriggerStage,
deploymentWithConfig: $deploymentWithConfig,
triggeredFromAppName: $triggeredFromAppName,
triggeredFromPipelineName: $triggeredFromPipelineName
}\')

curl -sS -X POST "$DevtronEndpoint/orchestrator/app/cd-pipeline/trigger" \\
-H "Content-Type: application/json" \\
-H "token: $DevtronApiToken" \\
--data "$jsonPayload" \\
--compressed
}

echo "Triggering CD Pipeline for App ID: $app_id, Pipeline ID: $pipeline_id, CI Artifact ID: $ciArtifactId, and CD Workflow Type: $TargetTriggerStage"
cd_pipeline=$(trigger_cd_pipeline "$pipeline_id" "$app_id" "$ciArtifactId")

check_deploy_status() {
if [ "$StatusTimeOutSeconds" -le "0" ]; then
echo "Skipping deployment status check. Taking StatusTimeOutSecond0 as a default input"
return
fi

local appId=$1
local pipelineId=$2
local max_wait=$StatusTimeOutSeconds
local statusKey="deploy_status" # Default status key

if [[ "$TargetTriggerStage" == "PRE" ]]; then
statusKey="pre_status"
elif [[ "$TargetTriggerStage" == "POST" ]]; then
statusKey="post_status"
fi

local start_time=$(date +%s)

while :; do
local current_time=$(date +%s)
local elapsed_time=$((current_time - start_time))

if [ "$elapsed_time" -ge "$max_wait" ]; then
echo "Timeout reached without success. Exiting..."
exit 1
fi

local statusUrl="$DevtronEndpoint/orchestrator/app/workflow/status/$appId/v2"
local response=$(curl -s -H "token: $DevtronApiToken" "$statusUrl")
local code=$(echo "$response" | jq -r \'.code\')

if [ "$code" != "200" ]; then
echo "Error: Received response - $response. Exiting..."
exit 1
fi
local status=$(echo "$response" | jq -r --arg pipelineId "$pipelineId" --arg statusKey "$statusKey" \'.result.cdWorkflowStatus[] | select(.pipeline_id == ($pipelineId | tonumber)) | .[$statusKey]\')

echo "Current $TargetTriggerStage status: $status"

if [[ "$status" == "Succeeded" ]]; then
echo "Deployment succeeded."
break
elif [[ "$status" == "Failed" ]]; then
echo "$TargetTriggerStage workflow failed."
exit 1
fi

sleep $sleepInterval
done
}

# Optionally check the deployment status based on the CD workflow type
check_deploy_status "$app_id" "$pipeline_id"'


,
'SHELL',
'f',
'now()',
1,
'now()',
1
);
INSERT INTO "plugin_step" ("id", "plugin_id","name","description","index","step_type","script_id","deleted", "created_on", "created_by", "updated_on", "updated_by") VALUES (nextval('id_seq_plugin_step'), (SELECT id FROM plugin_metadata WHERE name='Devtron CD Trigger v1.0.0'),'Step 1','Step 1 - Devtron CD Trigger v1.0.0','1','INLINE',(SELECT last_value FROM id_seq_plugin_pipeline_script),'f','now()', 1, 'now()', 1);

INSERT INTO plugin_step_variable (id,plugin_step_id,name,format,description,is_exposed,allow_empty_value,default_value,value,variable_type,value_type,previous_step_index,variable_step_index,variable_step_index_in_plugin,reference_variable_name,deleted,created_on,created_by,updated_on,updated_by)
VALUES
(nextval('id_seq_plugin_step_variable'),(SELECT ps.id FROM plugin_metadata p inner JOIN plugin_step ps on ps.plugin_id=p.id WHERE p.name='Devtron CD Trigger v1.0.0' and ps."index"=1 and ps.deleted=false),'DevtronApiToken','STRING','Enter Devtron API Token','t','f',null,null,'INPUT','NEW',null,1,null,null,'f','now()',1,'now()',1),
(nextval('id_seq_plugin_step_variable'),(SELECT ps.id FROM plugin_metadata p inner JOIN plugin_step ps on ps.plugin_id=p.id WHERE p.name='Devtron CD Trigger v1.0.0' and ps."index"=1 and ps.deleted=false),'DevtronEndpoint','STRING','Enter URL of Devtron','t','f',null,null,'INPUT','NEW',null,1,null,null,'f','now()',1,'now()',1),
(nextval('id_seq_plugin_step_variable'),(SELECT ps.id FROM plugin_metadata p inner JOIN plugin_step ps on ps.plugin_id=p.id WHERE p.name='Devtron CD Trigger v1.0.0' and ps."index"=1 and ps.deleted=false),'DevtronApp','STRING','Enter the Devtron Application name/Id','t','f',null,null,'INPUT','NEW',null,1,null,null,'f','now()',1,'now()',1),
(nextval('id_seq_plugin_step_variable'),(SELECT ps.id FROM plugin_metadata p inner JOIN plugin_step ps on ps.plugin_id=p.id WHERE p.name='Devtron CD Trigger v1.0.0' and ps."index"=1 and ps.deleted=false),'DevtronEnv','STRING','Enter the Environment name/Id','t','f',null,null,'INPUT','NEW',null,1,null,null,'f','now()',1,'now()',1),
(nextval('id_seq_plugin_step_variable'),(SELECT ps.id FROM plugin_metadata p inner JOIN plugin_step ps on ps.plugin_id=p.id WHERE p.name='Devtron CD Trigger v1.0.0' and ps."index"=1 and ps.deleted=false),'StatusTimeOutSeconds','STRING','Enter the maximum time (in seconds) a user can wait for the application to deploy.Enter a postive integer value','t','t',0,null,'INPUT','NEW',null,1,null,null,'f','now()',1,'now()',1),
(nextval('id_seq_plugin_step_variable'),(SELECT ps.id FROM plugin_metadata p inner JOIN plugin_step ps on ps.plugin_id=p.id WHERE p.name='Devtron CD Trigger v1.0.0' and ps."index"=1 and ps.deleted=false),'GitCommitHash','STRING','Enter the git hash from which user wants to deploy its application. By deault it takes latest Artifact ID to deploy the application','t','t',null,null,'INPUT','NEW',null,1,null,null,'f','now()',1,'now()',1),
(nextval('id_seq_plugin_step_variable'),(SELECT ps.id FROM plugin_metadata p inner JOIN plugin_step ps on ps.plugin_id=p.id WHERE p.name='Devtron CD Trigger v1.0.0' and ps."index"=1 and ps.deleted=false),'TargetTriggerStage','STRING','Enter Trigger Stage PRE/DEPLOY/POST, Default DEPLOY','t','t','DEPLOY',null,'INPUT','NEW',null,1,null,null,'f','now()',1,'now()',1);
Loading