Compare commits

..

24 commits

Author SHA1 Message Date
2682cca424 Update with real local dev options 2024-06-27 16:23:34 +02:00
edbe56055d Empty 2024-06-27 16:23:16 +02:00
d6a1f2b655 Update command for live exec 2024-06-27 16:23:01 +02:00
eb18eb03bc Add todo 2024-06-27 16:22:22 +02:00
8c8219b5fa Sleep 3 secs 2024-06-27 15:40:05 +02:00
24579506ae Linting 2024-06-27 14:24:53 +02:00
07169c5b3d Remove todos 2024-06-27 14:19:27 +02:00
872b29179e Update tests 2024-06-27 14:19:19 +02:00
a0dbb79d30 Add initial tests 2024-06-27 12:43:51 +02:00
26da85487f Add TODOs 2024-06-27 12:43:38 +02:00
d6b6cb7a72 Lint 2024-06-27 12:43:24 +02:00
ff61ff383e Rename to backend 2024-06-27 11:48:17 +02:00
170d9bc6d0 Implement missing methods 2024-06-27 11:42:09 +02:00
7f2c78ba97 Remove unused import, arguments 2024-06-27 11:41:42 +02:00
e72ac8882e Rename for better semantics 2024-06-27 11:40:01 +02:00
de23855d65 All upper case TODO 2024-06-27 11:39:45 +02:00
a6b6a6b8bc Typo 2024-06-27 10:18:44 +02:00
9cdd647514 Add resource files 2024-06-26 16:52:59 +02:00
f9f2389c95 Update provider with correct vars 2024-06-26 16:52:40 +02:00
ce9d3fdee6 Pass env credentials to start command 2024-06-26 16:52:04 +02:00
4eadad3940 Rename to TERRAFORM_BACKEND_GIT 2024-06-26 15:25:34 +02:00
6e5275b56b Implement backend git api and respective checks 2024-06-26 15:23:20 +02:00
14db8bed54 Adhere to naming convention 2024-06-26 13:12:08 +02:00
4539b84d6c WIP implement tf backend git provider 2024-06-26 10:35:53 +02:00
38 changed files with 249 additions and 174 deletions

View file

@ -87,3 +87,10 @@ kotlin-image-publish:
stage: image stage: image
script: script:
- cd infrastructure/kotlin && pyb image publish - cd infrastructure/kotlin && pyb image publish
backup-image-publish:
<<: *img
<<: *tag_only
stage: image
script:
- cd infrastructure/backup && pyb image publish

View file

@ -1,6 +1,6 @@
# dda-devops-build # dda-devops-build
[![Slack](https://img.shields.io/badge/chat-clojurians-green.svg?style=flat)](https://clojurians.slack.com/messages/#dda-pallet/) | [<img src="https://domaindrivenarchitecture.org/img/delta-chat.svg" width=20 alt="DeltaChat"> chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [<img src="https://meissa.de/images/parts/contact/mastodon36_hue9b2464f10b18e134322af482b9c915e_5501_filter_14705073121015236177.png" width=20 alt="M"> meissa@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@meissa) | [Blog](https://domaindrivenarchitecture.org) | [Website](https://meissa.de) [![Slack](https://img.shields.io/badge/chat-clojurians-green.svg?style=flat)](https://clojurians.slack.com/messages/#dda-pallet/) | [<img src="https://meissa-gmbh.de/img/community/Mastodon_Logotype.svg" width=20 alt="team@social.meissa-gmbh.de"> team@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@team) | [Website & Blog](https://domaindrivenarchitecture.org)
dda-devops-build integrates all the tools we use to work with clouds & provide some nice functions around. dda-devops-build integrates all the tools we use to work with clouds & provide some nice functions around.

View file

@ -33,7 +33,7 @@ default_task = "dev"
name = "ddadevops" name = "ddadevops"
MODULE = "not-used" MODULE = "not-used"
PROJECT_ROOT_PATH = "." PROJECT_ROOT_PATH = "."
version = "4.13.2-dev" version = "4.12.1-dev"
summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud" summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud"
description = __doc__ description = __doc__
authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")] authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")]
@ -97,6 +97,7 @@ def initialize(project):
"mixin_types": ["RELEASE"], "mixin_types": ["RELEASE"],
"release_primary_build_file": "build.py", "release_primary_build_file": "build.py",
"release_secondary_build_files": [ "release_secondary_build_files": [
"infrastructure/backup/build.py",
"infrastructure/python/build.py", "infrastructure/python/build.py",
"infrastructure/dind/build.py", "infrastructure/dind/build.py",
"infrastructure/ddadevops/build.py", "infrastructure/ddadevops/build.py",
@ -140,6 +141,12 @@ def lint(project):
shell=True, shell=True,
check=True, check=True,
) )
run(
"pylint -d W0511,R0903,C0301,W0614,C0114,C0115,C0116,similarities,W1203,W0702,W0702,"
+ "R0913,R0902,R0914,R1732,R1705,W0707,C0123,W0703,C0103 src/main/python/ddadevops/",
shell=True,
check=True,
)
@task @task

View file

@ -35,7 +35,6 @@ classDiagram
| release_organisation | Optional: The repository organisation name | | | release_organisation | Optional: The repository organisation name | |
| release_repository_name | Optional: The repository name name | | | release_repository_name | Optional: The repository name name | |
| release_artifacts | Optional: The list of artifacts to publish to the release generated name | [] | | release_artifacts | Optional: The list of artifacts to publish to the release generated name | [] |
| release_tag_prefix | Optional: Prefix of tag | "" |
## Example Usage just for creating releases ## Example Usage just for creating releases

View file

@ -1,15 +1,26 @@
# Dev Setup
## For local development
# For local development ```bash
```
python3 -m venv ~/.venv --upgrade python3 -m venv ~/.venv --upgrade
source ~/.venv/bin/activate source ~/.venv/bin/activate
pip3 install --upgrade -r dev_requirements.txt pip3 install --upgrade -r dev_requirements.txt
pip3 install --upgrade -r requirements.txt pip3 install --upgrade -r requirements.txt
``` ```
# For testing a dev version ## For testing a dev version
```
With uploading to pypi
```bash
pyb publish upload pyb publish upload
pip3 install --upgrade ddadevops --pre pip3 install --upgrade ddadevops --pre
``` ```
With locally installing the package
```bash
pyb publish
pip3 install --upgrade -e /home/${USER}/repo/opensource/dda-devops-build/target/dist/ddadevops-4.12.1-dev/
```

View file

@ -7,7 +7,7 @@ import logging
name = 'dda-backup' name = 'dda-backup'
MODULE = 'NOT_SET' MODULE = 'NOT_SET'
PROJECT_ROOT_PATH = '../..' PROJECT_ROOT_PATH = '../..'
version = "4.12.2-dev" version = "4.12.1-dev"
@init @init
@ -38,11 +38,6 @@ def image(project):
build = get_devops_build(project) build = get_devops_build(project)
build.image() build.image()
@task
def test(project):
build = get_devops_build(project)
build.test()
@task @task
def drun(project): def drun(project):
build = get_devops_build(project) build = get_devops_build(project)

View file

@ -42,7 +42,6 @@ function backup-fs-from-directory() {
} }
# Das tut so nicht!
function restore-directory() { function restore-directory() {
local directory="$1"; shift local directory="$1"; shift
local snapshot_id="${1:-latest}"; shift local snapshot_id="${1:-latest}"; shift

View file

@ -1,7 +0,0 @@
FROM dda-backup:latest
# install it
RUN apt update && apt install -qqy openjdk-17-jre-headless
ADD resources /tmp/
RUN rm -rf /root/.m2
RUN /tmp/install-test.bb

View file

@ -1,4 +0,0 @@
{:deps {org.clojure/spec.alpha {:mvn/version "0.4.233"}
orchestra/orchestra {:mvn/version "2021.01.01-1"}
org.domaindrivenarchitecture/dda-backup {:mvn/version "0.1.1-SNAPSHOT"}}}

View file

@ -1,32 +0,0 @@
#!/usr/bin/env bb
(require '[babashka.tasks :as tasks])
(defn curl-and-check!
[filename artifact-url sha256-url]
(let [filepath (str "/tmp/" filename)]
(tasks/shell "curl" "-SsLo" filepath artifact-url)
(tasks/shell "curl" "-SsLo" "/tmp/checksum" sha256-url)
(tasks/shell "bash" "-c" (str "echo \" " filepath "\"|tee -a /tmp/checksum"))
;(tasks/shell "sha256sum" "-c" "--status" "/tmp/checksum")
))
(defn tar-install!
[filename binname]
(let [filepath (str "/tmp/" filename)]
(tasks/shell "tar" "-C" "/tmp" "-xzf" filepath)
(tasks/shell "install" "-m" "0700" "-o" "root" "-g" "root" (str "/tmp/" binname) "/usr/local/bin/")))
(defn install!
[filename]
(tasks/shell "install" "-m" "0700" "-o" "root" "-g" "root" (str "/tmp/" filename) "/usr/local/bin/"))
(tasks/shell "bb" "/tmp/test.bb")
(curl-and-check!
"provs-syspec.jar"
"https://repo.prod.meissa.de/attachments/0a1da41e-aa5b-4a3e-a3b1-215cf2d5b021"
"https://repo.prod.meissa.de/attachments/f227cf65-cb0f-46a7-a6cd-28f46917412a")
(install! "provs-syspec.jar")
(tasks/shell "apt" "update")
(tasks/shell "apt" "install" "-qqy" "openjdk-17-jre-headless")
(tasks/shell "java" "-jar" "/usr/local/bin/provs-syspec.jar" "local" "-c" "/tmp/spec.yml" )

View file

@ -1,7 +0,0 @@
package:
- name: "restic"
command:
- command: "bb -h"
- command: "/tmp/test.bb"

View file

@ -1,27 +0,0 @@
#!/usr/bin/env bb
(require '[babashka.tasks :as tasks]
'[dda.backup.management :as mgm])
(defn restic-repo-init!
[]
(spit "restic-pwd" "ThePassword")
(mgm/init! {:password-file "restic-pwd"
:restic-repository "restic-repo"}))
(defn restic-backup!
[]
(tasks/shell "mkdir" "test-backup")
(spit "test-backup/file" "I was here")
(tasks/shell "restic" "backup" "--password-file" "restic-pwd" "--repo" "restic-repo" "test-backup"))
(defn restic-restore!
[]
(tasks/shell "mkdir" "test-restore")
(tasks/shell "restic" "restore" "--password-file" "restic-pwd" "--repo" "restic-repo" "--target" "test-restore" "latest")
)
(restic-repo-init!)
(restic-backup!)
(restic-restore!)

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = "ddadevops" name = "ddadevops"
MODULE = "clj-cljs" MODULE = "clj-cljs"
PROJECT_ROOT_PATH = "../.." PROJECT_ROOT_PATH = "../.."
version = "4.13.2-dev" version = "4.12.1-dev"
@init @init
def initialize(project): def initialize(project):

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = "ddadevops" name = "ddadevops"
MODULE = "clj" MODULE = "clj"
PROJECT_ROOT_PATH = "../.." PROJECT_ROOT_PATH = "../.."
version = "4.13.2-dev" version = "4.12.1-dev"
@init @init
def initialize(project): def initialize(project):

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = "ddadevops" name = "ddadevops"
MODULE = "ddadevops" MODULE = "ddadevops"
PROJECT_ROOT_PATH = "../.." PROJECT_ROOT_PATH = "../.."
version = "4.13.2-dev" version = "4.12.1-dev"
@init @init

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = "ddadevops" name = "ddadevops"
MODULE = "dind" MODULE = "dind"
PROJECT_ROOT_PATH = "../.." PROJECT_ROOT_PATH = "../.."
version = "4.13.2-dev" version = "4.12.1-dev"
@init @init

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = "ddadevops" name = "ddadevops"
MODULE = "kotlin" MODULE = "kotlin"
PROJECT_ROOT_PATH = "../.." PROJECT_ROOT_PATH = "../.."
version = "4.13.2-dev" version = "4.12.1-dev"
@init @init

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = "ddadevops" name = "ddadevops"
MODULE = "python" MODULE = "python"
PROJECT_ROOT_PATH = "../.." PROJECT_ROOT_PATH = "../.."
version = "4.13.2-dev" version = "4.12.1-dev"
@init @init

View file

@ -53,8 +53,7 @@ class ReleaseService:
bump_version = release_version.create_bump() bump_version = release_version.create_bump()
release_message = f"release: {release_version.to_string()}" release_message = f"release: {release_version.to_string()}"
bump_message = f"bump version to: {bump_version.to_string()}" bump_message = f"bump version to: {bump_version.to_string()}"
release_tag = f"{release.release_tag_prefix}{release_version.to_string()}" self.git_api.tag_annotated(release_version.to_string(), release_message, 0)
self.git_api.tag_annotated(release_tag, release_message, 0)
self.__set_version_and_commit__( self.__set_version_and_commit__(
bump_version, bump_version,
release.build_files(), release.build_files(),

View file

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

View file

@ -13,44 +13,52 @@ class DevopsTerraformBuild(DevopsBuild):
inp["mixin_types"] = config.get("mixin_types", []) inp["mixin_types"] = config.get("mixin_types", [])
super().__init__(project, inp) super().__init__(project, inp)
project.build_depends_on("dda-python-terraform") project.build_depends_on("dda-python-terraform")
self.teraform_service = TerraformService.prod() self.terraform_service = TerraformService.prod()
# TODO: we might want to make this private in the future, keeping this for compatibility
def initialize_build_dir(self): def initialize_build_dir(self):
super().initialize_build_dir() super().initialize_build_dir()
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
self.teraform_service.initialize_build_dir(devops) self.terraform_service.initialize_build_dir(devops)
def pre_build(self):
self.initialize_build_dir()
devops = self.devops_repo.get_devops(self.project)
if self.terraform_service.uses_backend_git(devops):
self.terraform_service.start_tf_backend_git_daemon(devops)
# TODO: Do we want a time.sleep(1) for usability?
def post_build(self): def post_build(self):
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
self.teraform_service.post_build(devops) self.terraform_service.post_build(devops)
def read_output_json(self) -> map: def read_output_json(self) -> map:
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
return self.teraform_service.read_output(devops) return self.terraform_service.read_output(devops)
def plan(self): def plan(self):
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
self.teraform_service.plan(devops) self.terraform_service.plan(devops)
self.post_build() self.post_build()
def plan_fail_on_diff(self): def plan_fail_on_diff(self):
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
self.teraform_service.plan(devops, fail_on_diff=True) self.terraform_service.plan(devops, fail_on_diff=True)
self.post_build() self.post_build()
def apply(self, auto_approve=False): def apply(self, auto_approve=False):
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
self.teraform_service.apply(devops, auto_approve=auto_approve) self.terraform_service.apply(devops, auto_approve=auto_approve)
self.post_build() self.post_build()
def refresh(self): def refresh(self):
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
self.teraform_service.refresh(devops) self.terraform_service.refresh(devops)
self.post_build() self.post_build()
def destroy(self, auto_approve=False): def destroy(self, auto_approve=False):
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
self.teraform_service.destroy(devops, auto_approve=auto_approve) self.terraform_service.destroy(devops, auto_approve=auto_approve)
self.post_build() self.post_build()
def tf_import( def tf_import(
@ -59,5 +67,5 @@ class DevopsTerraformBuild(DevopsBuild):
tf_import_resource, tf_import_resource,
): ):
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
self.teraform_service.tf_import(devops, tf_import_name, tf_import_resource) self.terraform_service.tf_import(devops, tf_import_name, tf_import_resource)
self.post_build() self.post_build()

View file

@ -15,6 +15,7 @@ from .terraform import TerraformDomain
from .provider_digitalocean import Digitalocean from .provider_digitalocean import Digitalocean
from .provider_hetzner import Hetzner from .provider_hetzner import Hetzner
from .provider_aws import Aws from .provider_aws import Aws
from .backend_tf_backend_git import TerraformBackendGit
from .provs_k3s import K3s from .provs_k3s import K3s
from .release import Release from .release import Release
from .artifact import Artifact from .artifact import Artifact

View file

@ -0,0 +1,72 @@
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__(),
"lock_address": self.__make_http_backend_address__(),
"unlock_address": self.__make_http_backend_address__(),
}
def resources_from_package(self) -> Set[str]:
return {"tf_backend_git_backend.tf", "tf_backend_git_backend_vars.tf"}
# TODO: This can not be used for backend, as the backend block can not reference vars.
# This is another reason to introduce a 'backend' object
def project_vars(self) -> Dict[str, Any]:
return {
"": ""
}
def is_local_state(self):
return False
def __make_http_backend_address__(self) -> str:
# TODO Should we make this configurable?
base_string = "http://localhost:6061/?type=git"
state = f"{self.stage}/{self.module}/{self.git_backend_state}"
return f"{base_string}&repository={self.git_backend_repo}&ref={self.git_backend_ref}&state={state}"
# TODO: Implement ssh auth too
@classmethod
def get_mapping_default(cls) -> List[Dict[str, str]]:
return [
{
"gopass_path": "server/meissa/repo/terraform-backend-git-test",
"gopass_field": "user",
"name": "git_backend_username",
},
{
"gopass_path": "server/meissa/repo/terraform-backend-git-test",
"gopass_field": "token",
"name": "git_backend_token",
},
]

View file

@ -11,7 +11,6 @@ class BuildFileType(Enum):
JS = ".json" JS = ".json"
JAVA_GRADLE = ".gradle" JAVA_GRADLE = ".gradle"
JAVA_CLOJURE = ".clj" JAVA_CLOJURE = ".clj"
JAVA_CLOJURE_EDN = ".edn"
PYTHON = ".py" PYTHON = ".py"
@ -42,8 +41,6 @@ class BuildFile(Validateable):
result = BuildFileType.JAVA_CLOJURE result = BuildFileType.JAVA_CLOJURE
case ".py": case ".py":
result = BuildFileType.PYTHON result = BuildFileType.PYTHON
case ".edn":
result = BuildFileType.JAVA_CLOJURE_EDN
case _: case _:
result = None result = None
return result return result
@ -51,13 +48,11 @@ class BuildFile(Validateable):
def __get_file_type_regex_str(self, file_type: BuildFileType): def __get_file_type_regex_str(self, file_type: BuildFileType):
match file_type: match file_type:
case BuildFileType.JAVA_GRADLE: case BuildFileType.JAVA_GRADLE:
return r"(?P<pre_version>\bversion\s?=\s?)\"(?P<version>\d*\.\d*\.\d*(-SNAPSHOT)?)\"" return r'(?P<pre_version>\bversion\s?=\s?)\"(?P<version>\d*\.\d*\.\d*(-SNAPSHOT)?)\"'
case BuildFileType.PYTHON: case BuildFileType.PYTHON:
return r"(?P<pre_version>\bversion\s?=\s?)\"(?P<version>\d*\.\d*\.\d*(-SNAPSHOT|-dev\d*)?)\"" return r'(?P<pre_version>\bversion\s?=\s?)\"(?P<version>\d*\.\d*\.\d*(-SNAPSHOT|-dev\d*)?)\"'
case BuildFileType.JAVA_CLOJURE: case BuildFileType.JAVA_CLOJURE:
return r"(?P<pre_version>\(defproject\s(\S)*\s)\"(?P<version>\d*\.\d*\.\d*(-SNAPSHOT)?)\"" return r'(?P<pre_version>\(defproject\s(\S)*\s)\"(?P<version>\d*\.\d*\.\d*(-SNAPSHOT)?)\"'
case BuildFileType.JAVA_CLOJURE_EDN:
return r"(?P<pre_version>\:version\s+)\"(?P<version>\d*\.\d*\.\d*(-SNAPSHOT)?)\""
case _: case _:
return "" return ""
@ -67,15 +62,8 @@ class BuildFile(Validateable):
match build_file_type: match build_file_type:
case BuildFileType.JS: case BuildFileType.JS:
version_str = json.loads(self.content)["version"] version_str = json.loads(self.content)["version"]
case ( case BuildFileType.JAVA_GRADLE | BuildFileType.PYTHON | BuildFileType.JAVA_CLOJURE:
BuildFileType.JAVA_GRADLE version_str = re.search(self.__get_file_type_regex_str(build_file_type), self.content).group("version")
| BuildFileType.PYTHON
| BuildFileType.JAVA_CLOJURE
| BuildFileType.JAVA_CLOJURE_EDN
):
version_str = re.search(
self.__get_file_type_regex_str(build_file_type), self.content
).group("version")
except: except:
raise RuntimeError(f"Version not found in file {self.file_path}") raise RuntimeError(f"Version not found in file {self.file_path}")
@ -96,15 +84,10 @@ class BuildFile(Validateable):
json_data = json.loads(self.content) json_data = json.loads(self.content)
json_data["version"] = new_version.to_string() json_data["version"] = new_version.to_string()
self.content = json.dumps(json_data, indent=4) self.content = json.dumps(json_data, indent=4)
case ( case BuildFileType.JAVA_GRADLE | BuildFileType.PYTHON | BuildFileType.JAVA_CLOJURE:
BuildFileType.JAVA_GRADLE
| BuildFileType.PYTHON
| BuildFileType.JAVA_CLOJURE
| BuildFileType.JAVA_CLOJURE_EDN
):
substitute = re.sub( substitute = re.sub(
self.__get_file_type_regex_str(build_file_type), self.__get_file_type_regex_str(build_file_type),
rf'\g<pre_version>"{new_version.to_string()}"', fr'\g<pre_version>"{new_version.to_string()}"',
self.content, self.content,
1, 1,
) )

View file

@ -13,10 +13,12 @@ class BuildType(Enum):
TERRAFORM = 3 TERRAFORM = 3
# TODO: We could follow domain implications and make a 'BackendType' enum
class ProviderType(Enum): class ProviderType(Enum):
DIGITALOCEAN = 0 DIGITALOCEAN = 0
HETZNER = 1 HETZNER = 1
AWS = 2 AWS = 2
TERRAFORM_BACKEND_GIT = 3
class MixinType(Enum): class MixinType(Enum):
@ -78,12 +80,6 @@ class DnsRecord(Validateable):
result.append("ipv4 & ipv6 may not both be empty.") result.append("ipv4 & ipv6 may not both be empty.")
return result return result
def ip(self) -> str:
if (self.ipv4):
return self.ipv4
else:
return self.ipv6
class Devops(Validateable): class Devops(Validateable):
def __init__( def __init__(

View file

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

View file

@ -86,7 +86,7 @@ class K3s(Validateable):
cmd = [ cmd = [
"provs-server.jar", "provs-server.jar",
"k3s", "k3s",
f"{self.k3s_provision_user}@{self.provision_dns.ip()}", f"{self.k3s_provision_user}@{self.provision_dns.fqdn}",
"-c", "-c",
f"{devops.build_path()}/out_k3sServerConfig.yaml", f"{devops.build_path()}/out_k3sServerConfig.yaml",
"-a", "-a",

View file

@ -24,7 +24,6 @@ class Release(Validateable):
"release_secondary_build_files", [] "release_secondary_build_files", []
) )
self.version = version self.version = version
self.release_tag_prefix = inp.get("release_tag_prefix", "")
self.release_artifact_server_url = inp.get("release_artifact_server_url") self.release_artifact_server_url = inp.get("release_artifact_server_url")
self.release_organisation = inp.get("release_organisation") self.release_organisation = inp.get("release_organisation")
self.release_repository_name = inp.get("release_repository_name") self.release_repository_name = inp.get("release_repository_name")

View file

@ -8,6 +8,7 @@ from .common import (
from .provider_digitalocean import Digitalocean from .provider_digitalocean import Digitalocean
from .provider_hetzner import Hetzner from .provider_hetzner import Hetzner
from .provider_aws import Aws from .provider_aws import Aws
from .backend_tf_backend_git import TerraformBackendGit
class TerraformDomain(Validateable): class TerraformDomain(Validateable):
@ -42,6 +43,8 @@ class TerraformDomain(Validateable):
self.providers[ProviderType.HETZNER] = Hetzner(inp) self.providers[ProviderType.HETZNER] = Hetzner(inp)
if ProviderType.AWS in provider_types: if ProviderType.AWS in provider_types:
self.providers[ProviderType.AWS] = Aws(inp) 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]: def validate(self) -> List[str]:
result = [] result = []
@ -86,6 +89,21 @@ class TerraformDomain(Validateable):
result = result and provider.is_local_state() result = result and provider.is_local_state()
return result return result
def uses_backend_git(self) -> bool:
if ProviderType.TERRAFORM_BACKEND_GIT in self.providers:
return True
return False
# TODO: Add ssh method case and make this default
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]: def backend_config(self) -> Dict[str, Any]:
result = {} result = {}
for provider in self.providers.values(): for provider in self.providers.values():

View file

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

View file

@ -1,4 +1,5 @@
from subprocess import Popen, PIPE, run, CalledProcessError from subprocess import Popen, PIPE, run, CalledProcessError
from typing import Dict
from pathlib import Path from pathlib import Path
from sys import stdout from sys import stdout
from os import chmod, environ from os import chmod, environ
@ -216,6 +217,20 @@ class TerraformApi:
pass 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_live(f"{env} nohup terraform-backend-git &")
def stop(self):
self.execution_api.execute("terraform-backend-git stop")
class ArtifactDeploymentApi: class ArtifactDeploymentApi:
def __init__(self): def __init__(self):
self.execution_api = ExecutionApi() self.execution_api = ExecutionApi()

View file

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

View file

@ -24,7 +24,7 @@ def devops_config(overrides: dict) -> dict:
"k3s_letsencrypt_endpoint": "k3s_letsencrypt_endpoint", "k3s_letsencrypt_endpoint": "k3s_letsencrypt_endpoint",
"k3s_enable_echo": "false", "k3s_enable_echo": "false",
"k3s_app_filename_to_provision": "k3s_app.yaml", "k3s_app_filename_to_provision": "k3s_app.yaml",
"tf_provider_types": ["DIGITALOCEAN", "HETZNER", "AWS"], "tf_provider_types": ["DIGITALOCEAN", "HETZNER", "AWS", "TERRAFORM_BACKEND_GIT"],
"tf_additional_vars": [], "tf_additional_vars": [],
"tf_output_json_name": "the_out.json", "tf_output_json_name": "the_out.json",
"tf_use_workspace": None, "tf_use_workspace": None,
@ -58,6 +58,11 @@ def devops_config(overrides: dict) -> dict:
"release_artifact_server_url": None, "release_artifact_server_url": None,
"release_organisation": None, "release_organisation": None,
"release_repository_name": None, "release_repository_name": None,
"git_backend_repo": "https://repo.example.com/meissa/infra-states",
"git_backend_ref": "main",
"git_backend_state": "test.json",
"git_backend_username": "tf_backend_user",
"git_backend_token": "324asd234df435sfdgh",
"credentials_mappings": [ "credentials_mappings": [
{ {
"gopass_path": "a/path", "gopass_path": "a/path",

View file

@ -183,34 +183,6 @@ def test_should_parse_and_set_version_for_clj():
== sut.content == sut.content
) )
def test_should_parse_and_set_version_for_clj_edn():
sut = BuildFile(
Path("./deps.edn"),
"""
{:project {:name org.domaindrivenarchitecture/dda-backup
:version "1.1.5-SNAPSHOT"}
}
""",
)
assert sut.get_version() == Version.from_str("1.1.5-SNAPSHOT", "SNAPSHOT")
sut = BuildFile(
Path("./deps.edn"),
"""
{:project {:name org.domaindrivenarchitecture/dda-backup
:version "1.1.5-SNAPSHOT"}
}
""",
)
sut.set_version(Version.from_str("1.1.5-SNAPSHOT", "SNAPSHOT").create_major())
assert (
'\n{:project {:name org.domaindrivenarchitecture/dda-backup\n :version "2.0.0"}\n\n}\n'
== sut.content
)
def test_should_throw_for_clj_wrong_version(): def test_should_throw_for_clj_wrong_version():
sut = BuildFile( sut = BuildFile(
Path("./project.clj"), Path("./project.clj"),

View file

@ -0,0 +1,30 @@
from pybuilder.core import Project
from pathlib import Path
from src.main.python.ddadevops.domain import (
BuildType,
TerraformBackendGit,
)
from .helper import devops_config
def test_tf_backend_git_creation():
sut = TerraformBackendGit(
{
"module": "module",
"stage": "test",
"git_backend_repo": "https://repo.example.com/meissa/infra-states",
"git_backend_ref": "main",
"git_backend_state": "test.json",
"git_backend_username": "tf_backend_user",
"git_backend_token": "324asd234df435sfdgh",
}
)
assert sut is not None
assert sut.is_valid()
def test_should_calculate_backend_config():
sut = TerraformBackendGit(devops_config({}))
assert {
"address": "http://localhost:6061/?type=git&repository=https://repo.example.com/meissa/infra-states&ref=main&state=test/module/test.json",
"lock_address": "http://localhost:6061/?type=git&repository=https://repo.example.com/meissa/infra-states&ref=main&state=test/module/test.json",
"unlock_address": "http://localhost:6061/?type=git&repository=https://repo.example.com/meissa/infra-states&ref=main&state=test/module/test.json",
} == sut.backend_config()

View file

@ -24,7 +24,7 @@ def test_should_calculate_command():
assert ( assert (
"provs-server.jar " "provs-server.jar "
+ "k3s " + "k3s "
+ "k3s_provision_user@::1 " + "k3s_provision_user@example.org "
+ "-c " + "-c "
+ "root_path/target/name/module/out_k3sServerConfig.yaml " + "root_path/target/name/module/out_k3sServerConfig.yaml "
+ "-a " + "-a "

View file

@ -17,6 +17,7 @@ def test_creation():
assert sut.providers[ProviderType.DIGITALOCEAN] assert sut.providers[ProviderType.DIGITALOCEAN]
assert sut.providers[ProviderType.HETZNER] assert sut.providers[ProviderType.HETZNER]
assert sut.providers[ProviderType.AWS] assert sut.providers[ProviderType.AWS]
assert sut.providers[ProviderType.TERRAFORM_BACKEND_GIT]
def test_should_calculate_output_json_name(): def test_should_calculate_output_json_name():
@ -77,6 +78,7 @@ def test_should_calculate_project_vars():
{ {
"do_as_backend": False, "do_as_backend": False,
"aws_as_backend": False, "aws_as_backend": False,
"tf_provider_types": ["DIGITALOCEAN", "HETZNER", "AWS"]
} }
) )
sut = TerraformDomain(config) sut = TerraformDomain(config)
@ -166,6 +168,17 @@ def test_should_calculate_resources_from_package():
"aws_provider_vars.tf", "aws_provider_vars.tf",
"aws_backend_wkms_vars.tf", "aws_backend_wkms_vars.tf",
"my.file", "my.file",
"tf_backend_git_backend_vars.tf",
"tf_backend_git_backend.tf",
} == sut.resources_from_package()
config = devops_config({"tf_provider_types": ["TERRAFORM_BACKEND_GIT"]})
sut = TerraformDomain(config)
assert {
"versions.tf",
"terraform_build_vars.tf",
"tf_backend_git_backend_vars.tf",
"tf_backend_git_backend.tf",
} == sut.resources_from_package() } == sut.resources_from_package()
@ -188,3 +201,7 @@ def test_should_calculate_local_state_handling():
) )
) )
assert not sut.is_local_state() assert not sut.is_local_state()
def test_should_use_backend_git():
sut = TerraformDomain(devops_config({}))
assert sut.uses_backend_git()