refactor devops_build to arch

This commit is contained in:
Michael Jerger 2023-03-12 17:40:10 +01:00
parent 3bdc75cdca
commit d95827e1e0
8 changed files with 110 additions and 66 deletions

View file

@ -28,7 +28,7 @@ use_plugin("python.distutils")
default_task = "publish" default_task = "publish"
name = "ddadevops" name = "ddadevops"
version = "3.1.3" version = "3.2.0-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")]

View file

@ -7,4 +7,9 @@ classDiagram
do_sth(project) do_sth(project)
} }
class ProjectRepository {
get_build(project): Build
set_build(project, build)
}
``` ```

View file

@ -20,6 +20,7 @@ from .devops_build import DevopsBuild, create_devops_build_config, get_devops_bu
from .credential import gopass_password_from_path, gopass_field_from_path from .credential import gopass_password_from_path, gopass_field_from_path
from .domain import Validateable, Build, DockerBuild, C4kBuild from .domain import Validateable, Build, DockerBuild, C4kBuild
from .application import BuildService, DockerBuildService, C4kBuildService from .application import DockerBuildService, C4kBuildService
from .infrastructure import ProjectRepository, ResourceApi, FileApi, DockerApi, ExecutionApi
__version__ = "${version}" __version__ = "${version}"

View file

@ -2,14 +2,6 @@ from .domain import Build, DockerBuild, C4kBuild
from .infrastructure import FileApi, ResourceApi, DockerApi, ExecutionApi from .infrastructure import FileApi, ResourceApi, DockerApi, ExecutionApi
class BuildService:
def __init__(self):
self.file_api = FileApi()
def initialize_build_dir(self, build: Build):
self.file_api.clean_dir(build.build_path())
class DockerBuildService: class DockerBuildService:
def __init__(self): def __init__(self):
self.file_api = FileApi() self.file_api = FileApi()

View file

@ -1,5 +1,6 @@
import deprecation
from .domain import Build from .domain import Build
from .application import BuildService from .infrastructure import ProjectRepository, FileApi
def create_devops_build_config( def create_devops_build_config(
@ -12,7 +13,8 @@ def create_devops_build_config(
"build_dir_name": build_dir_name, "build_dir_name": build_dir_name,
} }
@deprecation.deprecated(deprecated_in="3.2")
# Do not expose build to outside
def get_devops_build(project): def get_devops_build(project):
return project.get_property("devops_build") return project.get_property("devops_build")
@ -33,15 +35,20 @@ def get_tag_from_latest_commit():
class DevopsBuild: class DevopsBuild:
def __init__(self, project, config): def __init__(self, project, config):
self.build = Build(project, config) self.project = project
self.build_service = BuildService() self.file_api = FileApi()
project.set_property("devops_build", self) self.repo = ProjectRepository()
config.update({"name": project.name})
build = Build(config)
self.repo.set_build(self.project, build)
def name(self): def name(self):
return self.build.name() build = self.repo.get_build(self.project)
return build.name
def build_path(self): def build_path(self):
return self.build.build_path() build = self.repo.get_build(self.project)
return build.build_path()
def initialize_build_dir(self): def initialize_build_dir(self):
self.build_service.initialize_build_dir(self.build) self.file_api.clean_dir(build.build_path())

View file

@ -1,3 +1,4 @@
import deprecation
from typing import List from typing import List
from .python_util import filter_none from .python_util import filter_none
@ -18,26 +19,29 @@ class Validateable:
class Build(Validateable): class Build(Validateable):
def __init__(self, project, config): def __init__(self, config):
self.stage = config["stage"] self.stage = config["stage"]
self.name = config["name"]
self.project_root_path = config["project_root_path"] self.project_root_path = config["project_root_path"]
self.module = config["module"] self.module = config["module"]
self.build_dir_name = config["build_dir_name"] self.build_dir_name = config["build_dir_name"]
# Deprecated - no longer use generic stack ...
self.stack = {} self.stack = {}
self.project = project
@deprecation.deprecated(deprecated_in="3.2")
# use .name instead
def name(self): def name(self):
return self.project.name return self.name
def build_path(self): def build_path(self):
path = [self.project_root_path, self.build_dir_name, self.name(), self.module] path = [self.project_root_path, self.build_dir_name, self.name, self.module]
return "/".join(filter_none(path)) return "/".join(filter_none(path))
# TODO: these functions should be located at TerraformBuild later on. # TODO: these functions should be located at TerraformBuild later on.
def update_runtime_config(self, fqdn, ipv4, ipv6): def update_runtime_config(self, fqdn, ipv4, ipv6):
self.__put__('fqdn', fqdn) self.__put__("fqdn", fqdn)
self.__put__('ipv4', ipv4) self.__put__("ipv4", ipv4)
self.__put__('ipv6', ipv6) self.__put__("ipv6", ipv6)
def __put__(self, key, value): def __put__(self, key, value):
self.stack[key] = value self.stack[key] = value
@ -78,15 +82,17 @@ class C4kBuild(Validateable):
self.c4k_mixin_config = config["C4kMixin"]["config"] self.c4k_mixin_config = config["C4kMixin"]["config"]
self.c4k_mixin_auth = config["C4kMixin"]["auth"] self.c4k_mixin_auth = config["C4kMixin"]["auth"]
tmp = self.c4k_mixin_config["mon-cfg"] tmp = self.c4k_mixin_config["mon-cfg"]
tmp.update({"cluster-name": self.build.module, "cluster-stage": self.build.stage}) tmp.update(
{"cluster-name": self.build.module, "cluster-stage": self.build.stage}
)
self.c4k_mixin_config.update({"mon-cfg": tmp}) self.c4k_mixin_config.update({"mon-cfg": tmp})
def update_runtime_config(self, fqdn, ipv4, ipv6): def update_runtime_config(self, fqdn, ipv4, ipv6):
self.build.update_runtime_config(fqdn, ipv4, ipv6) self.build.update_runtime_config(fqdn, ipv4, ipv6)
def config(self): def config(self):
fqdn = self.build.__get__('fqdn') fqdn = self.build.__get__("fqdn")
self.c4k_mixin_config.update({'fqdn': fqdn}) self.c4k_mixin_config.update({"fqdn": fqdn})
return self.c4k_mixin_config return self.c4k_mixin_config
def command(self): def command(self):

View file

@ -3,22 +3,33 @@ from sys import stdout
from pkg_resources import resource_string from pkg_resources import resource_string
from os import chmod from os import chmod
import yaml import yaml
from .domain import Build
from .python_util import execute from .python_util import execute
class ResourceApi():
class ProjectRepository:
def get_build(self, project) -> Build:
return project.get_property("devops_build")
def set_build(self, project, build: Build):
project.set_property("devops_build", build)
class ResourceApi:
def read_resource(self, path: str) -> bytes: def read_resource(self, path: str) -> bytes:
return resource_string(__name__, path) return resource_string(__name__, path)
class FileApi():
class FileApi:
def clean_dir(self, directory: str): def clean_dir(self, directory: str):
execute('rm -rf ' + directory, shell=True) execute("rm -rf " + directory, shell=True)
execute('mkdir -p ' + directory, shell=True) execute("mkdir -p " + directory, shell=True)
def cp_force(self, src: str, target_dir: str): def cp_force(self, src: str, target_dir: str):
execute('cp -f ' + src + '* ' + target_dir, shell=True) execute("cp -f " + src + "* " + target_dir, shell=True)
def cp_recursive(self, src: str, target_dir: str): def cp_recursive(self, src: str, target_dir: str):
execute('cp -r ' + src + ' ' + target_dir, shell=True) execute("cp -r " + src + " " + target_dir, shell=True)
def write_data_to_file(self, path: Path, data: bytes): def write_data_to_file(self, path: Path, data: bytes):
with open(path, "w", encoding="utf-8") as output_file: with open(path, "w", encoding="utf-8") as output_file:
@ -29,37 +40,56 @@ class FileApi():
yaml.dump(data, output_file) yaml.dump(data, output_file)
chmod(path, 0o600) chmod(path, 0o600)
class DockerApi():
class DockerApi:
def image(self, name: str, path: Path): def image(self, name: str, path: Path):
execute('docker build -t ' + name + execute(
' --file ' + path + '/image/Dockerfile ' "docker build -t "
+ path + '/image', shell=True) + name
+ " --file "
+ path
+ "/image/Dockerfile "
+ path
+ "/image",
shell=True,
)
def drun(self, name: str): def drun(self, name: str):
execute('docker run -it --entrypoint="" ' + execute('docker run -it --entrypoint="" ' + name + " /bin/bash", shell=True)
name + ' /bin/bash', shell=True)
def dockerhub_login(self, username: str, password: str): def dockerhub_login(self, username: str, password: str):
execute('docker login --username ' + username + execute(
' --password ' + password, shell=True) "docker login --username " + username + " --password " + password,
shell=True,
)
def dockerhub_publish(self, name: str, username: str, tag=None): def dockerhub_publish(self, name: str, username: str, tag=None):
if tag is not None: if tag is not None:
execute('docker tag ' + name + ' ' + username + execute(
'/' + name + ':' + tag, shell=True) "docker tag " + name + " " + username + "/" + name + ":" + tag,
execute('docker push ' + username + shell=True,
'/' + name + ':' + tag, shell=True) )
execute('docker tag ' + name + ' ' + username + execute("docker push " + username + "/" + name + ":" + tag, shell=True)
'/' + name + ':latest', shell=True) execute(
execute('docker push ' + username + "docker tag " + name + " " + username + "/" + name + ":latest", shell=True
'/' + name + ':latest', shell=True) )
execute("docker push " + username + "/" + name + ":latest", shell=True)
def test(self, name: str, path: Path): def test(self, name: str, path: Path):
execute('docker build -t ' + name + '-test ' + execute(
'--file ' + path + '/test/Dockerfile ' "docker build -t "
+ path + '/test', shell=True) + name
+ "-test "
+ "--file "
+ path
+ "/test/Dockerfile "
+ path
+ "/test",
shell=True,
)
class ExecutionApi():
class ExecutionApi:
def execute(command: str, dry_run=False): def execute(command: str, dry_run=False):
output = "" output = ""
if dry_run: if dry_run:

View file

@ -42,9 +42,10 @@ def test_validate_with_reason():
def test_c4k_build_should_update_fqdn(tmp_path): def test_c4k_build_should_update_fqdn(tmp_path):
project = Project(str(tmp_path), name="test-project") project = Project(str(tmp_path), name="name")
project_config = { project_config = {
"stage": "test", "stage": "test",
"name": "name",
"project_root_path": str(tmp_path), "project_root_path": str(tmp_path),
"module": "module", "module": "module",
"build_dir_name": "target", "build_dir_name": "target",
@ -62,7 +63,7 @@ def test_c4k_build_should_update_fqdn(tmp_path):
grafana_cloud_user="user", grafana_cloud_user="user",
grafana_cloud_password="password", grafana_cloud_password="password",
) )
sut = C4kBuild(Build(project, project_config), project, project_config) sut = C4kBuild(Build(project_config), project, project_config)
sut.update_runtime_config("test.de", None, None) sut.update_runtime_config("test.de", None, None)
assert { assert {
@ -88,9 +89,10 @@ def test_c4k_build_should_update_fqdn(tmp_path):
def test_c4k_build_should_calculate_command(tmp_path): def test_c4k_build_should_calculate_command(tmp_path):
project = Project(str(tmp_path), name="test-project") project = Project(str(tmp_path), name="name")
project_config = { project_config = {
"stage": "test", "stage": "test",
"name": "name",
"project_root_path": "", "project_root_path": "",
"module": "module", "module": "module",
"build_dir_name": "target", "build_dir_name": "target",
@ -102,17 +104,18 @@ def test_c4k_build_should_calculate_command(tmp_path):
grafana_cloud_user="user", grafana_cloud_user="user",
grafana_cloud_password="password", grafana_cloud_password="password",
) )
sut = C4kBuild(Build(project, project_config), project, project_config) sut = C4kBuild(Build(project_config), project, project_config)
assert ( assert (
"c4k-module-standalone.jar " "c4k-module-standalone.jar "
+ "/target/test-project/module/out_c4k_config.yaml " + "/target/name/module/out_c4k_config.yaml "
+ "/target/test-project/module/out_c4k_auth.yaml > " + "/target/name/module/out_c4k_auth.yaml > "
+ "/target/test-project/module/out_module.yaml" + "/target/name/module/out_module.yaml"
== sut.command() == sut.command()
) )
project_config = { project_config = {
"stage": "test", "stage": "test",
"name": "name",
"project_root_path": "", "project_root_path": "",
"module": "module", "module": "module",
"build_dir_name": "target", "build_dir_name": "target",
@ -125,12 +128,12 @@ def test_c4k_build_should_calculate_command(tmp_path):
grafana_cloud_user="user", grafana_cloud_user="user",
grafana_cloud_password="password", grafana_cloud_password="password",
) )
sut = C4kBuild(Build(project, project_config), project, project_config) sut = C4kBuild(Build(project_config), project, project_config)
assert ( assert (
"c4k-executabel_name-standalone.jar " "c4k-executabel_name-standalone.jar "
+ "/target/test-project/module/out_c4k_config.yaml " + "/target/name/module/out_c4k_config.yaml "
+ "/target/test-project/module/out_c4k_auth.yaml > " + "/target/name/module/out_c4k_auth.yaml > "
+ "/target/test-project/module/out_module.yaml" + "/target/name/module/out_module.yaml"
== sut.command() == sut.command()
) )