def test_sync_apps_missing_bootstrap_element_in_bootstrap_yaml(self): self.yaml_file_load_mock.side_effect = lambda file_path: { "/tmp/root-config-repo/bootstrap/values.yaml": { }, # empty bootstrap yaml }[file_path] args = SyncAppsCommand.Args( username="******", password="******", git_user="******", git_email="GIT_EMAIL", root_organisation="ROOT_ORGA", root_repository_name="ROOT_REPO", organisation="TEAM_ORGA", repository_name="TEAM_REPO", git_provider=GitProvider.GITHUB, git_provider_url=None, ) try: SyncAppsCommand(ARGS).execute() self.fail() except GitOpsException as ex: self.assertEqual( "Cannot find key 'bootstrap' in 'bootstrap/values.yaml'", str(ex))
def test_sync_apps_app_name_collission(self): self.yaml_file_load_mock.side_effect = lambda file_path: { "/tmp/root-config-repo/bootstrap/values.yaml": { "bootstrap": [{ "name": "team-non-prod" }, { "name": "other-team-non-prod" }], }, "/tmp/root-config-repo/apps/team-non-prod.yaml": { "repository": "https://team.config.repo.git", "applications": { "some-other-app-1": None }, }, "/tmp/root-config-repo/apps/other-team-non-prod.yaml": { "repository": "https://other-team.config.repo.git", "applications": { "my-app": None }, # the other-team already has an app named "my-app" }, }[file_path] try: SyncAppsCommand(ARGS).execute() self.fail() except GitOpsException as ex: self.assertEqual( "Application 'my-app' already exists in a different repository", str(ex))
def test_sync_apps_happy_flow(self): SyncAppsCommand(ARGS).execute() assert self.mock_manager.method_calls == [ call.GitRepoApiFactory.create(ARGS, "TEAM_ORGA", "TEAM_REPO",), call.GitRepoApiFactory.create(ARGS, "ROOT_ORGA", "ROOT_REPO",), call.GitRepo(self.team_config_git_repo_api_mock), call.GitRepo(self.root_config_git_repo_api_mock), call.GitRepo_team.get_clone_url(), call.logging.info("Team config repository: %s", "https://team.config.repo.git"), call.GitRepo_root.get_clone_url(), call.logging.info("Root config repository: %s", "https://root.config.repo.git"), call.GitRepo_team.clone(), call.GitRepo_team.get_full_file_path("."), call.os.listdir("/tmp/team-config-repo/."), call.os.path.join("/tmp/team-config-repo/.", "my-app"), call.os.path.isdir("/tmp/team-config-repo/./my-app"), call.logging.info("Found %s app(s) in apps repository: %s", 1, "my-app"), call.logging.info("Searching apps repository in root repository's 'apps/' directory..."), call.GitRepo_root.clone(), call.GitRepo_root.get_full_file_path("bootstrap/values.yaml"), call.yaml_file_load("/tmp/root-config-repo/bootstrap/values.yaml"), call.GitRepo_team.get_clone_url(), call.logging.info("Analyzing %s in root repository", "apps/team-non-prod.yaml"), call.GitRepo_root.get_full_file_path("apps/team-non-prod.yaml"), call.yaml_file_load("/tmp/root-config-repo/apps/team-non-prod.yaml"), call.logging.info("Found apps repository in %s", "apps/team-non-prod.yaml"), call.logging.info("Analyzing %s in root repository", "apps/other-team-non-prod.yaml"), call.GitRepo_root.get_full_file_path("apps/other-team-non-prod.yaml"), call.yaml_file_load("/tmp/root-config-repo/apps/other-team-non-prod.yaml"), call.logging.info("Sync applications in root repository's %s.", "apps/team-non-prod.yaml"), call.merge_yaml_element("/tmp/root-config-repo/apps/team-non-prod.yaml", "applications", {"my-app": {}}), call.GitRepo_team.get_author_from_last_commit(), call.GitRepo_root.commit("GIT_USER", "GIT_EMAIL", "author updated apps/team-non-prod.yaml"), call.GitRepo_root.push(), ]
def test_sync_apps_bootstrap_yaml_not_found(self): self.yaml_file_load_mock.side_effect = FileNotFoundError() try: SyncAppsCommand(ARGS).execute() self.fail() except GitOpsException as ex: self.assertEqual("File 'bootstrap/values.yaml' not found in root repository.", str(ex))
def test_sync_apps_invalid_bootstrap_entry_in_bootstrap_yaml(self): self.yaml_file_load_mock.side_effect = lambda file_path: { "/tmp/root-config-repo/bootstrap/values.yaml": { "bootstrap": [{"something": "invalid"}], # bootstrap entry has no "name" property }, }[file_path] try: SyncAppsCommand(ARGS).execute() self.fail() except GitOpsException as ex: self.assertEqual("Every bootstrap entry must have a 'name' property.", str(ex))
def test_sync_apps_missing_repository_element_in_team_yaml(self): self.yaml_file_load_mock.side_effect = lambda file_path: { "/tmp/root-config-repo/bootstrap/values.yaml": {"bootstrap": [{"name": "team-non-prod"}],}, "/tmp/root-config-repo/apps/team-non-prod.yaml": { # missing: "repository": "https://team.config.repo.git", "applications": {}, }, }[file_path] try: SyncAppsCommand(ARGS).execute() self.fail() except GitOpsException as ex: self.assertEqual("Cannot find key 'repository' in 'apps/team-non-prod.yaml'", str(ex))
def test_sync_apps_undefined_team_repo(self): self.yaml_file_load_mock.side_effect = lambda file_path: { "/tmp/root-config-repo/bootstrap/values.yaml": {"bootstrap": [{"name": "other-team-non-prod"}],}, "/tmp/root-config-repo/apps/other-team-non-prod.yaml": { "repository": "https://other-team.config.repo.git", # there is no repo matching the command's team repo "applications": {}, }, }[file_path] try: SyncAppsCommand(ARGS).execute() self.fail() except GitOpsException as ex: self.assertEqual( "Couldn't find config file for apps repository in root repository's 'apps/' directory", str(ex) )
def test_sync_apps_team_yaml_not_found(self): def file_load_mock_side_effect(file_path): if file_path == "/tmp/root-config-repo/bootstrap/values.yaml": return { "bootstrap": [{"name": "team-non-prod"}], } if file_path == "/tmp/root-config-repo/apps/team-non-prod.yaml": raise FileNotFoundError() raise Exception("test should not reach this") self.yaml_file_load_mock.side_effect = file_load_mock_side_effect try: SyncAppsCommand(ARGS).execute() self.fail() except GitOpsException as ex: self.assertEqual("File 'apps/team-non-prod.yaml' not found in root repository.", str(ex))
def test_sync_apps_already_up_to_date(self): self.yaml_file_load_mock.side_effect = lambda file_path: { "/tmp/root-config-repo/bootstrap/values.yaml": { "bootstrap": [{"name": "team-non-prod"}, {"name": "other-team-non-prod"}], }, "/tmp/root-config-repo/apps/team-non-prod.yaml": { "repository": "https://team.config.repo.git", "applications": {"my-app": None}, # my-app already exists }, "/tmp/root-config-repo/apps/other-team-non-prod.yaml": { "repository": "https://other-team.config.repo.git", "applications": {}, }, }[file_path] SyncAppsCommand(ARGS).execute() assert self.mock_manager.method_calls == [ call.GitRepoApiFactory.create(ARGS, "TEAM_ORGA", "TEAM_REPO",), call.GitRepoApiFactory.create(ARGS, "ROOT_ORGA", "ROOT_REPO",), call.GitRepo(self.team_config_git_repo_api_mock), call.GitRepo(self.root_config_git_repo_api_mock), call.GitRepo_team.get_clone_url(), call.logging.info("Team config repository: %s", "https://team.config.repo.git"), call.GitRepo_root.get_clone_url(), call.logging.info("Root config repository: %s", "https://root.config.repo.git"), call.GitRepo_team.clone(), call.GitRepo_team.get_full_file_path("."), call.os.listdir("/tmp/team-config-repo/."), call.os.path.join("/tmp/team-config-repo/.", "my-app"), call.os.path.isdir("/tmp/team-config-repo/./my-app"), call.logging.info("Found %s app(s) in apps repository: %s", 1, "my-app"), call.logging.info("Searching apps repository in root repository's 'apps/' directory..."), call.GitRepo_root.clone(), call.GitRepo_root.get_full_file_path("bootstrap/values.yaml"), call.yaml_file_load("/tmp/root-config-repo/bootstrap/values.yaml"), call.GitRepo_team.get_clone_url(), call.logging.info("Analyzing %s in root repository", "apps/team-non-prod.yaml"), call.GitRepo_root.get_full_file_path("apps/team-non-prod.yaml"), call.yaml_file_load("/tmp/root-config-repo/apps/team-non-prod.yaml"), call.logging.info("Found apps repository in %s", "apps/team-non-prod.yaml"), call.logging.info("Analyzing %s in root repository", "apps/other-team-non-prod.yaml"), call.GitRepo_root.get_full_file_path("apps/other-team-non-prod.yaml"), call.yaml_file_load("/tmp/root-config-repo/apps/other-team-non-prod.yaml"), call.logging.info("Root repository already up-to-date. I'm done here."), ]
import logging import os import unittest from unittest.mock import call from gitopscli.git_api import GitProvider, GitRepo, GitRepoApi, GitRepoApiFactory from gitopscli.commands.sync_apps import SyncAppsCommand from gitopscli.io_api.yaml_util import merge_yaml_element, yaml_file_load from gitopscli.gitops_exception import GitOpsException from .mock_mixin import MockMixin ARGS = SyncAppsCommand.Args( username="******", password="******", git_user="******", git_email="GIT_EMAIL", root_organisation="ROOT_ORGA", root_repository_name="ROOT_REPO", organisation="TEAM_ORGA", repository_name="TEAM_REPO", git_provider=GitProvider.GITHUB, git_provider_url=None, ) class SyncAppsCommandTest(MockMixin, unittest.TestCase): def setUp(self): self.init_mock_manager(SyncAppsCommand) self.os_mock = self.monkey_patch(os) self.os_mock.path.isdir.return_value = True self.os_mock.path.join.side_effect = os.path.join self.os_mock.listdir.return_value = ["my-app"]