Compare commits

..

6 commits

11 changed files with 122 additions and 47 deletions

View file

@ -3,17 +3,18 @@ from dda_python_terraform import Terraform, IsFlagged
from packaging import version
from ..domain import Devops, BuildType
from ..infrastructure import FileApi, ResourceApi, TerraformApi
from ..infrastructure import FileApi, ResourceApi, TerraformApi, TerraformBackendGitApi
# TODO: mv more fkt to Terraform_api ?
class TerraformService:
def __init__(
self, file_api: FileApi, resource_api: ResourceApi, terraform_api: TerraformApi
self, file_api: FileApi, resource_api: ResourceApi, terraform_api: TerraformApi, tf_backend_git_api: TerraformBackendGitApi
):
self.file_api = file_api
self.resource_api = resource_api
self.terraform_api = terraform_api
self.tf_backend_git_api = tf_backend_git_api
@classmethod
def prod(cls):
@ -21,6 +22,7 @@ class TerraformService:
FileApi(),
ResourceApi(),
TerraformApi(),
TerraformBackendGitApi(),
)
def initialize_build_dir(self, devops: Devops):
@ -35,6 +37,15 @@ class TerraformService:
self.file_api.cp("*.tfvars", devops.build_path(), check=False)
self.file_api.cp_recursive("scripts", devops.build_path(), check=False)
def start_tf_backend_git_daemon(self, devops: Devops):
terraform = devops.specialized_builds[BuildType.TERRAFORM]
credentials = terraform.env_credentials()
self.tf_backend_git_api.start(credentials)
def uses_backend_git(self, devops: Devops) -> bool:
terraform = devops.specialized_builds[BuildType.TERRAFORM]
return terraform.uses_backend_git()
def read_output(self, devops: Devops) -> map:
terraform_domain = devops.specialized_builds[BuildType.TERRAFORM]
return self.file_api.read_json_fro_file(
@ -154,6 +165,8 @@ class TerraformService:
def post_build(self, devops: Devops):
self.__rescue_local_state__(devops)
self.tf_backend_git_api.stop()
def __copy_build_resource_file_from_package__(self, resource_name, devops: Devops):
data = self.resource_api.read_resource(

View file

@ -14,12 +14,18 @@ class DevopsTerraformBuild(DevopsBuild):
super().__init__(project, inp)
project.build_depends_on("dda-python-terraform")
self.teraform_service = TerraformService.prod()
# ToDo: we might want to make this private in the future, keeping this for compatibility
def initialize_build_dir(self):
super().initialize_build_dir()
devops = self.devops_repo.get_devops(self.project)
self.teraform_service.initialize_build_dir(devops)
def pre_build(self):
self.initialize_build_dir()
devops = self.devops_repo.get_devops(self.project)
if self.teraform_service.uses_backend_git(devops):
self.teraform_service.start_tf_backend_git_daemon()
def post_build(self):
devops = self.devops_repo.get_devops(self.project)
self.teraform_service.post_build(devops)

View file

@ -17,6 +17,7 @@ class ProviderType(Enum):
DIGITALOCEAN = 0
HETZNER = 1
AWS = 2
TERRAFORM_BACKEND_GIT = 3
class MixinType(Enum):

View file

@ -6,6 +6,7 @@ from .devops_factory import DevopsFactory
from .terraform import TerraformDomain
from .provider_digitalocean import Digitalocean
from .provider_hetzner import Hetzner
from .provider_tf_backend_git import TerraformBackendGit
from .c4k import C4k
from .image import Image
from .release import ReleaseType, Release
@ -60,6 +61,8 @@ class InitService:
default_mappings += Digitalocean.get_mapping_default()
if ProviderType.HETZNER in provider_types:
default_mappings += Hetzner.get_mapping_default()
if ProviderType.TERRAFORM_BACKEND_GIT in provider_types:
default_mappings += TerraformBackendGit.get_mapping_default()
if MixinType.RELEASE in mixin_types:
primary_build_file_id = inp.get(

View file

@ -1,44 +0,0 @@
from typing import List, Dict, Set, Any
from .common import Validateable, CredentialMappingDefault
class TerraformGitBackend(Validateable, CredentialMappingDefault):
def __init__(
self,
inp: dict,
):
self.stage = inp.get("stage")
self.module = inp.get("module")
self.git_backend_repo = inp.get("git_backend_repo")
self.git_backend_ref = inp.get("git_backend_ref")
self.git_backend_state = inp.get("git_backend_state")
self.git_backend_username = inp.get("git_backend_username")
self.git_backend_token = inp.get("git_backend_token")
def validate(self) -> List[str]:
result = []
result += self.__validate_is_not_empty__("stage")
result += self.__validate_is_not_empty__("module")
result += self.__validate_is_not_empty__("git_backend_repo")
result += self.__validate_is_not_empty__("git_backend_ref")
result += self.__validate_is_not_empty__("git_backend_state")
result += self.__validate_is_not_empty__("git_backend_username")
result += self.__validate_is_not_empty__("git_backend_token")
return result
def backend_config(self) -> Dict[str, Any]:
return {
"git_backend_repo": self.git_backend_repo,
"git_backend_ref": self.git_backend_ref,
"git_backend_state": self.git_backend_state,
"git_backend_username": self.git_backend_username,
"git_backend_token": self.git_backend_token,
}
def project_vars(self):
return {
"git_backend_token": self.git_backend_token,
"git_backend_username": self.git_backend_username,
}

View file

@ -0,0 +1,63 @@
from typing import List, Dict, Set, Any
from .common import Validateable, CredentialMappingDefault
class TerraformBackendGit(Validateable, CredentialMappingDefault):
def __init__(
self,
inp: dict,
):
self.stage = inp.get("stage")
self.module = inp.get("module")
self.git_backend_repo = inp.get("git_backend_repo")
self.git_backend_ref = inp.get("git_backend_ref")
self.git_backend_state = inp.get("git_backend_state")
self.git_backend_username = inp.get("git_backend_username")
self.git_backend_token = inp.get("git_backend_token")
def validate(self) -> List[str]:
result = []
result += self.__validate_is_not_empty__("stage")
result += self.__validate_is_not_empty__("module")
result += self.__validate_is_not_empty__("git_backend_repo")
result += self.__validate_is_not_empty__("git_backend_ref")
result += self.__validate_is_not_empty__("git_backend_state")
result += self.__validate_is_not_empty__("git_backend_username")
result += self.__validate_is_not_empty__("git_backend_token")
return result
# See: https://developer.hashicorp.com/terraform/language/settings/backends/configuration#command-line-key-value-pairs
# and https://github.com/plumber-cd/terraform-backend-git?tab=readme-ov-file#standalone-terraform-http-backend-mode
def backend_config(self) -> Dict[str, Any]:
return {
"address": self.__make_http_backend_address__(self.git_backend_repo, self.git_backend_ref, self.git_backend_state),
"lock_address": self.__make_http_backend_address__(self.git_backend_repo, self.git_backend_ref, self.git_backend_state),
"unlock_address": self.__make_http_backend_address__(self.git_backend_repo, self.git_backend_ref, self.git_backend_state),
}
def resources_from_package(self) -> List[str]:
return {"tf_backend_git_backend.tf", "tf_backend_git_backend_vars.tf"}
# ToDo: This can not be used for backend config, as the backend block can not reference vars.
def project_vars(self) -> Dict[str, Any]:
return {
"http_backend_address": self.__make_http_backend_address__(self.git_backend_ref, self.git_backend_repo, self.git_backend_state)
}
def is_local_state(self):
return False
# ToDo:
def __bucket_key__(self):
pass
# ToDo:
def __make_http_backend_address__(self):
pass
# ToDo:
@classmethod
def get_mapping_default():
pass

View file

@ -8,6 +8,7 @@ from .common import (
from .provider_digitalocean import Digitalocean
from .provider_hetzner import Hetzner
from .provider_aws import Aws
from .provider_tf_backend_git import TerraformBackendGit
class TerraformDomain(Validateable):
@ -42,6 +43,8 @@ class TerraformDomain(Validateable):
self.providers[ProviderType.HETZNER] = Hetzner(inp)
if ProviderType.AWS in provider_types:
self.providers[ProviderType.AWS] = Aws(inp)
if ProviderType.TERRAFORM_BACKEND_GIT in provider_types:
self.providers[ProviderType.TERRAFORM_BACKEND_GIT] = TerraformBackendGit(inp)
def validate(self) -> List[str]:
result = []
@ -85,6 +88,20 @@ class TerraformDomain(Validateable):
for provider in self.providers.values():
result = result and provider.is_local_state()
return result
def uses_backend_git(self) -> bool:
return ProviderType.TERRAFORM_BACKEND_GIT in self.providers.keys()
# ToDo: Add ssh method case and make this default
# ToDo: How do we get to the credentials?
def env_credentials(self) -> Dict[str, str]:
tf_backend_git = self.providers[ProviderType.TERRAFORM_BACKEND_GIT]
if tf_backend_git.git_backend_token != "":
return {
"GITHUB_TOKEN" : tf_backend_git.git_backend_token,
"GIT_USERNAME" : tf_backend_git.git_backend_username,
}
return {"" : ""}
def backend_config(self) -> Dict[str, Any]:
result = {}

View file

@ -7,6 +7,7 @@ from .infrastructure import (
CredentialsApi,
GitApi,
TerraformApi,
TerraformBackendGitApi,
ArtifactDeploymentApi,
)
from .repository import DevopsRepository, BuildFileRepository

View file

@ -1,4 +1,5 @@
from subprocess import Popen, PIPE, run, CalledProcessError
from typing import Dict
from pathlib import Path
from sys import stdout
from os import chmod, environ
@ -215,6 +216,16 @@ class GitApi:
class TerraformApi:
pass
class TerraformBackendGitApi:
def __init__(self):
self.execution_api = ExecutionApi()
def start(self, credentials: Dict[str, str]):
env = ""
for key in credentials:
env = env + f'{key}' + "=" + f'{credentials[key]}' + " "
self.execution_api.execute(f'{env}' + " " + "terraform-backend-git &")
def stop(self):
self.execution_api.execute("terraform-backend-git stop")
class ArtifactDeploymentApi:
def __init__(self):

View file

@ -0,0 +1,3 @@
terraform {
backend "http" {}
}

View file

@ -0,0 +1 @@
variable http_backend_address {}