Skip to content

Commit

Permalink
🔥feat: Add PoC terragrunt
Browse files Browse the repository at this point in the history
  • Loading branch information
D10S0VSkY-OSS committed Dec 10, 2023
1 parent 436009a commit b4caa16
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 87 deletions.
2 changes: 1 addition & 1 deletion sld-api-backend/src/stacks/domain/entities/stacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class StackBase(BaseModel):
git_repo: constr(strip_whitespace=True)
branch: constr(strip_whitespace=True) = "main"
squad_access: List[str] = ["*"]
iac_type: Optional[Literal["terraform", "tofu"]] = "terraform"
iac_type: Optional[Literal["terraform", "tofu", "terragrunt"]] = "terraform"
tf_version: constr(strip_whitespace=True) = "1.6.5"
tags: Optional[List[str]] = []
project_path: Optional[constr(strip_whitespace=True)] = Field("", example="")
Expand Down
16 changes: 16 additions & 0 deletions sld-api-backend/src/worker/domain/entities/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pydantic import BaseModel
from typing import Optional, Dict


class StructActionsBase(BaseModel):
name: str
stack_name: str
branch: str
environment: str
squad: str
iac_type: Optional[str] = "terraform"
version: str
secreto: Dict
variables_file: Optional[str]
project_path: Optional[str]
task_id: str
24 changes: 24 additions & 0 deletions sld-api-backend/src/worker/domain/interfaces/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import Type
from abc import ABC, abstractmethod
from src.worker.domain.services.command import command
from src.worker.domain.entities.actions import StructActionsBase


class Actions(ABC):
def __init__(self, params: StructActionsBase, command: Type[command] = command) -> None:
self.name = params.name
self.stack_name = params.stack_name
self.branch = params.branch
self.environment = params.environment
self.squad = params.squad
self.iac_type = params.iac_type
self.version = params.version
self.secreto = params.secreto
self.variables_file = params.variables_file
self.project_path = params.project_path
self.task_id = params.task_id
self.command = command

@abstractmethod
def execute_deployer_command(self, action: str) -> dict:
pass
7 changes: 4 additions & 3 deletions sld-api-backend/src/worker/domain/interfaces/provider.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from abc import ABC, abstractmethod
from abc import ABC


class IProviderArtifactRequirements(ABC):
@staticmethod
def artifact_download(name, stack_name, environment, squad, git_repo, branch, project_path):
# logic to download artifact
pass
pass
63 changes: 16 additions & 47 deletions sld-api-backend/src/worker/domain/services/provider.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# DI terraform provider
from src.worker.providers.hashicorp.actions import Actions
from src.worker.providers.hashicorp.actions import Terraform, Actions
from src.worker.providers.hashicorp.artifact import Artifact
from src.worker.providers.hashicorp.download import BinaryDownload
from src.worker.providers.hashicorp.templates import Backend, GetVars, Tfvars
from src.worker.domain.entities.worker import DeployParams, DownloadBinaryParams
from typing import Type



class ProviderRequirements:
Expand Down Expand Up @@ -73,53 +75,20 @@ def json_vars(

class ProviderActions:
"""
This class contains the typical methods of a deployment
This class contains the typical methods of a deployment.
"""

def plan(params: DeployParams, action: Actions = Actions) -> dict:
config_action = action(
params.name,
params.stack_name,
params.branch,
params.environment,
params.squad,
params.iac_type,
params.version,
params.secreto,
params.variables_file,
params.project_path,
params.task_id,
)
return config_action.execute_terraform_command("plan")
@staticmethod
def plan(params: DeployParams, action: Type[Actions] = Terraform) -> dict:
config_action = action(params)
return config_action.execute_deployer_command("plan")

def apply(params: DeployParams, action: Actions = Actions) -> dict:
config_action = action(
params.name,
params.stack_name,
params.branch,
params.environment,
params.squad,
params.iac_type,
params.version,
params.secreto,
params.variables_file,
params.project_path,
params.task_id,
)
return config_action.execute_terraform_command("apply")
@staticmethod
def apply(params: DeployParams, action: Type[Actions] = Terraform) -> dict:
config_action = action(params)
return config_action.execute_deployer_command("apply")

def destroy(params: DeployParams, action: Actions = Actions) -> dict:
config_action = action(
params.name,
params.stack_name,
params.branch,
params.environment,
params.squad,
params.iac_type,
params.version,
params.secreto,
params.variables_file,
params.project_path,
params.task_id,
)
return config_action.execute_terraform_command("destroy")
@staticmethod
def destroy(params: DeployParams, action: Type[Actions] = Terraform) -> dict:
config_action = action(params)
return config_action.execute_deployer_command("destroy")
113 changes: 81 additions & 32 deletions sld-api-backend/src/worker/providers/hashicorp/actions.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,81 @@
import os
from dataclasses import dataclass
import jmespath
import requests
from config.api import settings

from src.worker.domain.interfaces.actions import Actions
from src.worker.security.providers_credentials import secret, unsecret
from src.worker.domain.services.command import command

@dataclass
class StructBase:
name: str
stack_name: str
branch: str
environment: str
squad: str


@dataclass
class Actions(StructBase):
iac_type: str
version: str
secreto: dict
variables_file: str
project_path: str
task_id: str
subprocess_handler: command = command

def execute_terraform_command(self, action: str) -> dict:


class Terraform(Actions):

def execute_deployer_command(self, action: str) -> dict:
channel = self.task_id
try:
secret(self.stack_name, self.environment, self.squad, self.name, self.secreto)
deploy_state = f"{self.environment}_{self.stack_name}_{self.squad}_{self.name}"

variables_files = (
f"{self.name}.tfvars.json"
if not self.variables_file
else self.variables_file
)

if not self.project_path:
os.chdir(f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}")
else:
os.chdir(f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}")

init_command = f"/tmp/{self.version}/{self.iac_type} init -no-color -input=false --upgrade"
plan_command = f"/tmp/{self.version}/{self.iac_type} plan -input=false -refresh -no-color -var-file={variables_files} -out={self.name}.tfplan"
apply_command = f"/tmp/{self.version}/{self.iac_type} apply -input=false -auto-approve -no-color {self.name}.tfplan"
destroy_command = f"/tmp/{self.version}/{self.iac_type} destroy -input=false -auto-approve -no-color -var-file={variables_files}"
output = []
if action == "plan":
result, output_init = self.command(init_command, channel=channel)
result, output_plan = self.command(plan_command, channel=channel)
output = output_init + output_plan
if action == "apply":
result, output_apply = self.command(apply_command, channel=channel)
output = output + output_apply
elif action == "destroy":
result, output_init = self.command(init_command, channel=channel)
result, output_destroy = self.command(destroy_command, channel=channel)
output = output_init + output_destroy

unsecret(self.stack_name, self.environment, self.squad, self.name, self.secreto)

rc = result

output_data = {
"command": action,
"deploy": self.name,
"squad": self.squad,
"stack_name": self.stack_name,
"environment": self.environment,
"rc": rc,
"tfvars_files": self.variables_file,
"remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}",
"project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}",
"stdout": output,
}

return output_data

except Exception:
return {
"command": action,
"deploy": self.name,
"squad": self.squad,
"stack_name": self.stack_name,
"environment": self.environment,
"rc": 1,
"tfvars_files": self.variables_file,
"project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}",
"remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}",
"stdout": output,
}


class TerraGrunt(Actions):

def execute_deployer_command(self, action: str) -> dict:
channel = self.task_id
try:
secret(self.stack_name, self.environment, self.squad, self.name, self.secreto)
Expand All @@ -49,15 +98,15 @@ def execute_terraform_command(self, action: str) -> dict:
destroy_command = f"/tmp/{self.version}/{self.iac_type} destroy -input=false -auto-approve -no-color -var-file={variables_files}"
output = []
if action == "plan":
result, output_init = command(init_command, channel=channel)
result, output_plan = self.subprocess_handler(plan_command, channel=channel)
result, output_init = self.command(init_command, channel=channel)
result, output_plan = self.command(plan_command, channel=channel)
output = output_init + output_plan
if action == "apply":
result, output_apply = self.subprocess_handler(apply_command, channel=channel)
result, output_apply = self.command(apply_command, channel=channel)
output = output + output_apply
elif action == "destroy":
result, output_init = command(init_command, channel=channel)
result, output_destroy = self.subprocess_handler(destroy_command, channel=channel)
result, output_init = self.command(init_command, channel=channel)
result, output_destroy = self.command(destroy_command, channel=channel)
output = output_init + output_destroy

unsecret(self.stack_name, self.environment, self.squad, self.name, self.secreto)
Expand Down
27 changes: 25 additions & 2 deletions sld-api-backend/src/worker/providers/hashicorp/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,30 @@ def get(self) -> dict:
"rc": 0,
"stdout": "Download Binary file",
}

elif self.iac_type == "terragrunt":
logging.info(f"Downloading binary iac_type {self.iac_type} version {self.version}")
binary_directory = f"/tmp/{self.version}"
downloaded_binary_path = f"{binary_directory}/terragrunt_linux_amd64"
renamed_binary_path = f"{binary_directory}/terragrunt"
# Download Terraform binary if not already downloaded
if not os.path.exists(f"/tmp/{self.version}"):
os.mkdir(f"/tmp/{self.version}")
if not os.path.isfile(f"/tmp/{self.version}/terragrunt"):
binary_url = f"https://github.com/gruntwork-io/terragrunt/releases/download/{self.version}/terragrunt_linux_amd64"
response = requests.get(binary_url, stream=True, verify=False)
with open(downloaded_binary_path, 'wb') as file:
for chunk in response.iter_content(chunk_size=90000000):
file.write(chunk)
os.chmod(downloaded_binary_path, os.stat(downloaded_binary_path).st_mode | stat.S_IEXEC)
os.rename(downloaded_binary_path, renamed_binary_path)
else:
logging.error(f"Failed to download Terragrunt binary from {binary_url}")
return {"command": "binaryDownload", "rc": 1, "stdout": "Failed to download binary file"}
return {
"command": "binaryDownload",
"rc": 0,
"stdout": "Download Binary file",
}
except Exception as err:
logging.error(f"Error downloading binary {self.iac_type} error: {err}")
return {"command": self.iac_type + "Download", "rc": 1, "stdout": err}

3 changes: 1 addition & 2 deletions sld-dashboard/app/home/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class StackForm(FlaskForm):
)
iac_type = SelectField(
"IaC Type",
choices=[('', 'Select an IaC Type'), ('terraform', 'Terraform'), ('openTofu', 'openTofu')],
choices=[('', 'Select an IaC Type'), ('terraform', 'Terraform'), ('openTofu', 'openTofu'), ('terragrunt', 'TerraGrunt')],
validators=[validators.DataRequired()],
coerce=lambda x: 'tofu' if x == 'openTofu' else x
)
Expand Down Expand Up @@ -71,7 +71,6 @@ class StackForm(FlaskForm):
icon_selector = SelectField('Icon Selector', choices=[], validators=[DataRequired()])



class DeployForm(FlaskForm):
deploy_name = StringField(
"Deploy Name",
Expand Down
2 changes: 2 additions & 0 deletions sld-dashboard/app/home/templates/deploys-list.html
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,11 @@ <h2 class="h4">All Deploys</h2>
</td>
<td>
<div>
<span style="font-size: 12px; font-weight: bold;">Start:</span>
<span class="font-weight-bold">{{ deploy.start_time }}</span>
</div>
<div>
<span style="font-size: 12px; font-weight: bold;">Destroy:</span>
<span class="font-weight-bold">{{ deploy.destroy_time }}</span>
</div>
</td>
Expand Down

0 comments on commit b4caa16

Please sign in to comment.