From b58f67827a90cbdc9797640e2ea0b15041405eb2 Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Mon, 9 Aug 2021 15:16:59 -0700 Subject: [PATCH 01/13] add comments --- .../step_implementers/deploy/helm.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/ploigos_step_runner/step_implementers/deploy/helm.py diff --git a/src/ploigos_step_runner/step_implementers/deploy/helm.py b/src/ploigos_step_runner/step_implementers/deploy/helm.py new file mode 100644 index 00000000..f497a84d --- /dev/null +++ b/src/ploigos_step_runner/step_implementers/deploy/helm.py @@ -0,0 +1,18 @@ +"""`StepImplementer` for the `deploy` step using Helm. + +Step Configuration +------------------ +Step configuration expected as input to this step. +Could come from: +* static configuration +* runtime configuration +* previous step results + +Configuration Key | Required? | Default | Description +-----------------------------|-----------|---------|----------- +`chart` | Yes | | The chart argument can be either: a chart \ + reference('example/mariadb'), a path to a chart directory, a \ + packaged chart, or a fully qualified URL. +`release` | Yes | | Release tag. +`flags` | No | `[]` | Use flags to customize the installation behavior. +""" # pylint: disable=line-too-long \ No newline at end of file From 7dcd28519f9e85b85639bf068ad8decb5727430c Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 07:59:22 -0700 Subject: [PATCH 02/13] updated header comments --- .../step_implementers/deploy/helm.py | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/ploigos_step_runner/step_implementers/deploy/helm.py b/src/ploigos_step_runner/step_implementers/deploy/helm.py index f497a84d..763f8f3e 100644 --- a/src/ploigos_step_runner/step_implementers/deploy/helm.py +++ b/src/ploigos_step_runner/step_implementers/deploy/helm.py @@ -1,4 +1,5 @@ -"""`StepImplementer` for the `deploy` step using Helm. +"""`StepImplementer` for the `deploy` step using `helm secrets upgrade --install` so that it works for both an initial \ +upgrade of a preinstalled helm chart. Step Configuration ------------------ @@ -10,9 +11,22 @@ Configuration Key | Required? | Default | Description -----------------------------|-----------|---------|----------- -`chart` | Yes | | The chart argument can be either: a chart \ +`helm-chart` | Yes | | The chart argument can be either: a chart \ reference('example/mariadb'), a path to a chart directory, a \ packaged chart, or a fully qualified URL. -`release` | Yes | | Release tag. -`flags` | No | `[]` | Use flags to customize the installation behavior. -""" # pylint: disable=line-too-long \ No newline at end of file +`helm-release` | Yes | | Release tag. +`helm-flags` | No | `[]` | Use flags to customize the installation behavior. +""" # pylint: disable=line-too-long + +import os + + +DEFAULT_CONFIG = { + 'helm-flags' : '' +} + + +REQUIRED_CONFIG_OR_PREVIOUS_STEP_RESULT_ARTIFACT_KEYS = [ + 'helm-chart', + 'helm-release' +] \ No newline at end of file From 950fc962c29bfe27910f6ef25f9550cf790ff93e Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 13:11:20 -0700 Subject: [PATCH 03/13] add test file --- .../step_implementers/deploy/helm.py | 148 +++++++++++++++++- tests/step_implementers/deploy/test_helm.py | 52 ++++++ 2 files changed, 195 insertions(+), 5 deletions(-) create mode 100644 tests/step_implementers/deploy/test_helm.py diff --git a/src/ploigos_step_runner/step_implementers/deploy/helm.py b/src/ploigos_step_runner/step_implementers/deploy/helm.py index 763f8f3e..942b88fe 100644 --- a/src/ploigos_step_runner/step_implementers/deploy/helm.py +++ b/src/ploigos_step_runner/step_implementers/deploy/helm.py @@ -16,17 +16,155 @@ packaged chart, or a fully qualified URL. `helm-release` | Yes | | Release tag. `helm-flags` | No | `[]` | Use flags to customize the installation behavior. -""" # pylint: disable=line-too-long +""" # pylint: disable=line-too-long -import os +import sh +import sys +from ploigos_step_runner.step_implementer import StepImplementer +from ploigos_step_runner import StepResult, StepRunnerException +from ploigos_step_runner.utils.io import \ + create_sh_redirect_to_multiple_streams_fn_callback DEFAULT_CONFIG = { - 'helm-flags' : '' + 'helm-flags': [] } - REQUIRED_CONFIG_OR_PREVIOUS_STEP_RESULT_ARTIFACT_KEYS = [ 'helm-chart', 'helm-release' -] \ No newline at end of file +] + +class Helm(StepImplementer): + """`StepImplementer` for the `deploy` step using Helm. + """ + + def __init__( # pylint: disable=too-many-arguments + self, + workflow_result, + parent_work_dir_path, + config, + environment=None, + helm_chart=None, + helm_release=None, + helm_flags=[] + ): + self.__helm_chart = helm_chart + self.__helm_release = helm_release + self.__helm_flags = helm_flags + + super().__init__( + workflow_result=workflow_result, + parent_work_dir_path=parent_work_dir_path, + config=config, + environment=environment + ) + + @staticmethod + def step_implementer_config_defaults(): + """Getter for the StepImplementer's configuration defaults. + + Returns + ------- + dict + Default values to use for step configuration values. + + Notes + ----- + These are the lowest precedence configuration values. + """ + return DEFAULT_CONFIG + + @staticmethod + def _required_config_or_result_keys(): + """Getter for step configuration or previous step result artifacts that are required before + running this step. + + See Also + -------- + _validate_required_config_or_previous_step_result_artifact_keys + + Returns + ------- + array_list + Array of configuration keys or previous step result artifacts + that are required before running the step. + """ + return REQUIRED_CONFIG_OR_PREVIOUS_STEP_RESULT_ARTIFACT_KEYS + + def _validate_required_config_or_previous_step_result_artifact_keys(self): + """Validates that the required configuration keys or previous step result artifacts + are set and have valid values. + + Validates that: + * required configuration is provided + * either both helm-chart and helm-release were provided + + Raises + ------ + StepRunnerException + If step configuration or previous step result artifacts have invalid required values + """ + + super()._validate_required_config_or_previous_step_result_artifact_keys() + + @property + def helm_flags(self): + """Gets the helm chart flags for this step. + """ + return self.__helm_release + + @property + def helm_chart(self): + """Gets the helm chart name for this step. + """ + return self.__helm_chart + + @property + def helm_release(self): + """Gets the helm chart release version for this step. + """ + return self.__helm_release + + + def _run_step(self): # pylint: disable=too-many-locals + """Runs the step implemented by this StepImplementer. + + Returns + ------- + StepResult + Object containing the dictionary results of this step. + """ + step_result = StepResult.from_step_implementer(self) + + # install the helm chart + helm_output_file_path = self.write_working_file('helm_output.txt') + try: + # execute helm step (params come from config) + with open(helm_output_file_path, 'w') as helm_output_file: + sh.helm( # pylint: disable=no-member + 'secrets', 'upgrade', '--install', + self.helm_chart, + self.helm_release, + *self.helm_flags, + _out=create_sh_redirect_to_multiple_streams_fn_callback([ + sys.stdout, + helm_output_file + ]), + _err=create_sh_redirect_to_multiple_streams_fn_callback([ + sys.stderr, + helm_output_file + ]) + ) + except StepRunnerException as error: + step_result.success = False + step_result.message = "Error running helm. " \ + f"More details maybe found in 'helm-output' report artifact: {error}" + finally: + step_result.add_artifact( + description="Standard out and standard error from helm.", + name='helm-output', + value=helm_output_file_path + ) + + return step_result diff --git a/tests/step_implementers/deploy/test_helm.py b/tests/step_implementers/deploy/test_helm.py new file mode 100644 index 00000000..a4078a71 --- /dev/null +++ b/tests/step_implementers/deploy/test_helm.py @@ -0,0 +1,52 @@ +import os +import re +from io import IOBase + +from unittest.mock import Mock, patch +from tests.helpers.base_step_implementer_test_case import \ + BaseStepImplementerTestCase +from tests.helpers.test_utils import Any, create_sh_side_effect +from ploigos_step_runner.step_implementers.deploy.helm import Helm + + +# @patch("ploigos_step_runner.step_implementers.shared.NpmGeneric.__init__") +# class TestStepImplementerMavenTest___init__(BaseStepImplementerTestCase): +# def test_defaults(self, mock_super_init): +# workflow_result = WorkflowResult() +# parent_work_dir_path = '/fake/path' +# config = {} +# +# Helm( +# workflow_result=workflow_result, +# parent_work_dir_path=parent_work_dir_path, +# config=config, +# environment=None, +# helm_chart=None, +# helm_release=None +# ) +# +# mock_super_init.assert_called_once_with( +# workflow_result, +# parent_work_dir_path, +# config, +# environment=None, +# helm_chart=None, +# helm_release=None, +# helm_flags=[] +# ) + +class TestStepImplementerDeployHelmBase(BaseStepImplementerTestCase): + def create_step_implementer( + self, + step_config={}, + parent_work_dir_path='', + environment=None + ): + return self.create_given_step_implementer( + step_implementer=Helm, + step_config=step_config, + step_name='deploy', + implementer='Helm', + parent_work_dir_path=parent_work_dir_path, + environment=environment + ) \ No newline at end of file From 64f97bafa7343629fd1afe7c60f2315e621a6680 Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 17:07:48 -0700 Subject: [PATCH 04/13] fix lint errors --- .../step_implementers/deploy/helm.py | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/ploigos_step_runner/step_implementers/deploy/helm.py b/src/ploigos_step_runner/step_implementers/deploy/helm.py index 942b88fe..3f01b1c9 100644 --- a/src/ploigos_step_runner/step_implementers/deploy/helm.py +++ b/src/ploigos_step_runner/step_implementers/deploy/helm.py @@ -15,11 +15,12 @@ reference('example/mariadb'), a path to a chart directory, a \ packaged chart, or a fully qualified URL. `helm-release` | Yes | | Release tag. -`helm-flags` | No | `[]` | Use flags to customize the installation behavior. +`helm-flags` | No | `[]` | Use flags to customize the installation behavior. """ # pylint: disable=line-too-long -import sh + import sys +import sh from ploigos_step_runner.step_implementer import StepImplementer from ploigos_step_runner import StepResult, StepRunnerException @@ -44,14 +45,8 @@ def __init__( # pylint: disable=too-many-arguments workflow_result, parent_work_dir_path, config, - environment=None, - helm_chart=None, - helm_release=None, - helm_flags=[] + environment=None ): - self.__helm_chart = helm_chart - self.__helm_release = helm_release - self.__helm_flags = helm_flags super().__init__( workflow_result=workflow_result, @@ -92,39 +87,24 @@ def _required_config_or_result_keys(): """ return REQUIRED_CONFIG_OR_PREVIOUS_STEP_RESULT_ARTIFACT_KEYS - def _validate_required_config_or_previous_step_result_artifact_keys(self): - """Validates that the required configuration keys or previous step result artifacts - are set and have valid values. - - Validates that: - * required configuration is provided - * either both helm-chart and helm-release were provided - - Raises - ------ - StepRunnerException - If step configuration or previous step result artifacts have invalid required values - """ - - super()._validate_required_config_or_previous_step_result_artifact_keys() @property def helm_flags(self): """Gets the helm chart flags for this step. """ - return self.__helm_release + return self.get_value('helm-flags') @property def helm_chart(self): """Gets the helm chart name for this step. """ - return self.__helm_chart + return self.get_value('helm-chart') @property def helm_release(self): """Gets the helm chart release version for this step. """ - return self.__helm_release + return self.get_value('helm-release') def _run_step(self): # pylint: disable=too-many-locals From 86ee8484bddcc2f43902ae15d9cda0a2cce8d44c Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 17:13:32 -0700 Subject: [PATCH 05/13] remove unused properties --- .../step_implementers/deploy/helm.py | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/src/ploigos_step_runner/step_implementers/deploy/helm.py b/src/ploigos_step_runner/step_implementers/deploy/helm.py index 3f01b1c9..2796df26 100644 --- a/src/ploigos_step_runner/step_implementers/deploy/helm.py +++ b/src/ploigos_step_runner/step_implementers/deploy/helm.py @@ -87,26 +87,6 @@ def _required_config_or_result_keys(): """ return REQUIRED_CONFIG_OR_PREVIOUS_STEP_RESULT_ARTIFACT_KEYS - - @property - def helm_flags(self): - """Gets the helm chart flags for this step. - """ - return self.get_value('helm-flags') - - @property - def helm_chart(self): - """Gets the helm chart name for this step. - """ - return self.get_value('helm-chart') - - @property - def helm_release(self): - """Gets the helm chart release version for this step. - """ - return self.get_value('helm-release') - - def _run_step(self): # pylint: disable=too-many-locals """Runs the step implemented by this StepImplementer. @@ -124,9 +104,9 @@ def _run_step(self): # pylint: disable=too-many-locals with open(helm_output_file_path, 'w') as helm_output_file: sh.helm( # pylint: disable=no-member 'secrets', 'upgrade', '--install', - self.helm_chart, - self.helm_release, - *self.helm_flags, + self.get_value('helm-chart'), + self.get_value('helm-release'), + *self.get_value('helm-flags'), _out=create_sh_redirect_to_multiple_streams_fn_callback([ sys.stdout, helm_output_file From 6e887fd553d5475ceb6810173d50c5c568956064 Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 17:21:58 -0700 Subject: [PATCH 06/13] make sure the default configs are there --- tests/step_implementers/deploy/test_helm.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/step_implementers/deploy/test_helm.py b/tests/step_implementers/deploy/test_helm.py index a4078a71..2911c631 100644 --- a/tests/step_implementers/deploy/test_helm.py +++ b/tests/step_implementers/deploy/test_helm.py @@ -49,4 +49,15 @@ def create_step_implementer( implementer='Helm', parent_work_dir_path=parent_work_dir_path, environment=environment + ) + +class TestStepImplementerMavenTest_step_implementer_config_defaults( + BaseStepImplementerTestCase +): + def test_result(self): + self.assertEqual( + Helm.step_implementer_config_defaults(), + { + 'helm-flags': [] + } ) \ No newline at end of file From 70697f556a30e9708978355eded345046ed4bdfb Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 17:25:03 -0700 Subject: [PATCH 07/13] make sure the required fields are as designed --- tests/step_implementers/deploy/test_helm.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/step_implementers/deploy/test_helm.py b/tests/step_implementers/deploy/test_helm.py index 2911c631..a2f9ba90 100644 --- a/tests/step_implementers/deploy/test_helm.py +++ b/tests/step_implementers/deploy/test_helm.py @@ -60,4 +60,16 @@ def test_result(self): { 'helm-flags': [] } + ) + +class TestStepImplementerMavenTest__required_config_or_result_keys( + BaseStepImplementerTestCase +): + def test_result(self): + self.assertEqual( + Helm._required_config_or_result_keys(), + [ + 'helm-chart', + 'helm-release' + ] ) \ No newline at end of file From 7f61dd25fa7d7f15c6647ce17c69d5fa0598b8c4 Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 17:27:30 -0700 Subject: [PATCH 08/13] add base test --- tests/step_implementers/deploy/test_helm.py | 47 ++++++++++----------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/tests/step_implementers/deploy/test_helm.py b/tests/step_implementers/deploy/test_helm.py index a2f9ba90..1c976ab5 100644 --- a/tests/step_implementers/deploy/test_helm.py +++ b/tests/step_implementers/deploy/test_helm.py @@ -9,31 +9,28 @@ from ploigos_step_runner.step_implementers.deploy.helm import Helm -# @patch("ploigos_step_runner.step_implementers.shared.NpmGeneric.__init__") -# class TestStepImplementerMavenTest___init__(BaseStepImplementerTestCase): -# def test_defaults(self, mock_super_init): -# workflow_result = WorkflowResult() -# parent_work_dir_path = '/fake/path' -# config = {} -# -# Helm( -# workflow_result=workflow_result, -# parent_work_dir_path=parent_work_dir_path, -# config=config, -# environment=None, -# helm_chart=None, -# helm_release=None -# ) -# -# mock_super_init.assert_called_once_with( -# workflow_result, -# parent_work_dir_path, -# config, -# environment=None, -# helm_chart=None, -# helm_release=None, -# helm_flags=[] -# ) +@patch("ploigos_step_runner.step_implementers.deploy.Helm.__init__") +class TestStepImplementerMavenTest___init__(BaseStepImplementerTestCase): + def test_defaults(self, mock_super_init): + workflow_result = WorkflowResult() + parent_work_dir_path = '/fake/path' + config = {} + + Helm( + workflow_result=workflow_result, + parent_work_dir_path=parent_work_dir_path, + config=config, + environment=None, + helm_chart=None, + helm_release=None + ) + + mock_super_init.assert_called_once_with( + workflow_result, + parent_work_dir_path, + config, + environment=None + ) class TestStepImplementerDeployHelmBase(BaseStepImplementerTestCase): def create_step_implementer( From a4f139cf2173e608c15f9f055f19c29429b2d651 Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 17:49:46 -0700 Subject: [PATCH 09/13] add testing stubs --- tests/step_implementers/deploy/test_helm.py | 275 ++++++++++++++++++-- 1 file changed, 251 insertions(+), 24 deletions(-) diff --git a/tests/step_implementers/deploy/test_helm.py b/tests/step_implementers/deploy/test_helm.py index 1c976ab5..6ec3271f 100644 --- a/tests/step_implementers/deploy/test_helm.py +++ b/tests/step_implementers/deploy/test_helm.py @@ -3,34 +3,56 @@ from io import IOBase from unittest.mock import Mock, patch + +from src.ploigos_step_runner.results.workflow_result import WorkflowResult from tests.helpers.base_step_implementer_test_case import \ BaseStepImplementerTestCase -from tests.helpers.test_utils import Any, create_sh_side_effect +from testfixtures import TempDirectory from ploigos_step_runner.step_implementers.deploy.helm import Helm -@patch("ploigos_step_runner.step_implementers.deploy.Helm.__init__") -class TestStepImplementerMavenTest___init__(BaseStepImplementerTestCase): - def test_defaults(self, mock_super_init): - workflow_result = WorkflowResult() - parent_work_dir_path = '/fake/path' - config = {} - - Helm( - workflow_result=workflow_result, - parent_work_dir_path=parent_work_dir_path, - config=config, - environment=None, - helm_chart=None, - helm_release=None - ) - - mock_super_init.assert_called_once_with( - workflow_result, - parent_work_dir_path, - config, - environment=None - ) +# @patch("ploigos_step_runner.step_implementers.deploy.Helm.__init__") +# class TestStepImplementerMavenTest___init__(BaseStepImplementerTestCase): +# def test_defaults(self, mock_super_init): +# workflow_result = WorkflowResult() +# parent_work_dir_path = '/fake/path' +# config = { +# 'helm-chart': 'chart name', +# 'helm-release': 'release name' +# } +# +# Helm( +# workflow_result=workflow_result, +# parent_work_dir_path=parent_work_dir_path, +# config=config, +# environment=None +# ) +# +# mock_super_init.assert_called_once_with( +# workflow_result, +# parent_work_dir_path, +# config, +# environment=None +# ) +# +# def test_given_environment(self, mock_super_init): +# workflow_result = WorkflowResult() +# parent_work_dir_path = '/fake/path' +# config = {} +# +# Helm( +# workflow_result=workflow_result, +# parent_work_dir_path=parent_work_dir_path, +# config=config, +# environment='mock-env' +# ) +# +# mock_super_init.assert_called_once_with( +# workflow_result=workflow_result, +# parent_work_dir_path=parent_work_dir_path, +# config=config, +# environment='mock-env' +# ) class TestStepImplementerDeployHelmBase(BaseStepImplementerTestCase): def create_step_implementer( @@ -69,4 +91,209 @@ def test_result(self): 'helm-chart', 'helm-release' ] - ) \ No newline at end of file + ) + +@patch.object(Helm, '_run_maven_step') +@patch.object(Helm, 'write_working_file', return_value='/mock/mvn_output.txt') +class TestStepImplementerMavenPackage__run_step( + BaseStepImplementerTestCase +): + def create_step_implementer( + self, + step_config={}, + workflow_result=None, + parent_work_dir_path='' + ): + return self.create_given_step_implementer( + step_implementer=Helm, + step_config=step_config, + step_name='deploy', + implementer='Helm', + workflow_result=workflow_result, + parent_work_dir_path=parent_work_dir_path + ) + + # def test_success_single_packaged_artifact( + # self, + # mock_write_working_file, + # mock_run_maven_step + # ): + # with TempDirectory() as test_dir: + # parent_work_dir_path = os.path.join(test_dir.path, 'working') + # + # pom_file = os.path.join(test_dir.path, 'mock-pom.xml') + # step_config = { + # 'pom-file': pom_file + # } + # step_implementer = self.create_step_implementer( + # step_config=step_config, + # parent_work_dir_path=parent_work_dir_path, + # ) + # + # # setup sideeffects + # artifact_parent_dir = os.path.join(test_dir.path, 'target') + # package_artifact_names = [ + # f'my-app.jar' + # ] + # def run_maven_side_effect(mvn_output_file_path): + # os.makedirs(artifact_parent_dir, exist_ok=True) + # for artifact_name in package_artifact_names: + # artifact_path = os.path.join( + # artifact_parent_dir, + # artifact_name + # ) + # Path(artifact_path).touch() + # + # mock_run_maven_step.side_effect = run_maven_side_effect + # + # # run step + # actual_step_result = step_implementer._run_step() + # + # # create expected step result + # expected_step_result = StepResult( + # step_name='package', + # sub_step_name='MavenPackage', + # sub_step_implementer_name='MavenPackage' + # ) + # expected_step_result.add_artifact( + # description="Standard out and standard error from maven.", + # name='maven-output', + # value='/mock/mvn_output.txt' + # ) + # expected_step_result.add_artifact( + # name='packages', + # value=[{ + # 'path': os.path.join(artifact_parent_dir, 'my-app.jar') + # }] + # ) + # + # # verify step result + # self.assertEqual( + # actual_step_result, + # expected_step_result + # ) + # + # mock_write_working_file.assert_called_once() + # mock_run_maven_step.assert_called_with( + # mvn_output_file_path='/mock/mvn_output.txt' + # ) + # + # def test_success_multiple_packaged_artifact( + # self, + # mock_write_working_file, + # mock_run_maven_step + # ): + # with TempDirectory() as test_dir: + # parent_work_dir_path = os.path.join(test_dir.path, 'working') + # + # pom_file = os.path.join(test_dir.path, 'mock-pom.xml') + # step_config = { + # 'pom-file': pom_file + # } + # step_implementer = self.create_step_implementer( + # step_config=step_config, + # parent_work_dir_path=parent_work_dir_path, + # ) + # + # # setup sideeffects + # artifact_parent_dir = os.path.join(test_dir.path, 'target') + # package_artifact_names = [ + # f'my-app.jar', + # f'my-app.war' + # ] + # def run_maven_side_effect(mvn_output_file_path): + # os.makedirs(artifact_parent_dir, exist_ok=True) + # for artifact_name in package_artifact_names: + # artifact_path = os.path.join( + # artifact_parent_dir, + # artifact_name + # ) + # Path(artifact_path).touch() + # + # mock_run_maven_step.side_effect = run_maven_side_effect + # + # # run step + # actual_step_result = step_implementer._run_step() + # + # # create expected step result + # expected_step_result = StepResult( + # step_name='package', + # sub_step_name='MavenPackage', + # sub_step_implementer_name='MavenPackage' + # ) + # expected_step_result.add_artifact( + # description="Standard out and standard error from maven.", + # name='maven-output', + # value='/mock/mvn_output.txt' + # ) + # expected_step_result.add_artifact( + # name='packages', + # value=[ + # { + # 'path': os.path.join(artifact_parent_dir, 'my-app.jar') + # }, + # { + # 'path': os.path.join(artifact_parent_dir, 'my-app.war') + # } + # ] + # ) + # + # # verify step result + # self.assertEqual( + # actual_step_result, + # expected_step_result + # ) + # + # mock_write_working_file.assert_called_once() + # mock_run_maven_step.assert_called_with( + # mvn_output_file_path='/mock/mvn_output.txt' + # ) + # + # def test_fail_maven_run( + # self, + # mock_write_working_file, + # mock_run_maven_step + # ): + # with TempDirectory() as test_dir: + # parent_work_dir_path = os.path.join(test_dir.path, 'working') + # + # pom_file = os.path.join(test_dir.path, 'mock-pom.xml') + # step_config = { + # 'pom-file': pom_file + # } + # step_implementer = self.create_step_implementer( + # step_config=step_config, + # parent_work_dir_path=parent_work_dir_path, + # ) + # + # # run step with mock failure + # mock_run_maven_step.side_effect = StepRunnerException('Mock error running maven') + # actual_step_result = step_implementer._run_step() + # + # # create expected step result + # surefire_reports_dir = os.path.join(test_dir.path, 'target/surefire-reports') + # expected_step_result = StepResult( + # step_name='package', + # sub_step_name='MavenPackage', + # sub_step_implementer_name='MavenPackage' + # ) + # expected_step_result.success = False + # expected_step_result.message = "Error running 'maven package' to package artifacts. " \ + # "More details maybe found in 'maven-output' report artifact: " \ + # "Mock error running maven" + # expected_step_result.add_artifact( + # description="Standard out and standard error from maven.", + # name='maven-output', + # value='/mock/mvn_output.txt' + # ) + # + # # verify step result + # self.assertEqual( + # actual_step_result, + # expected_step_result + # ) + # + # mock_write_working_file.assert_called_once() + # mock_run_maven_step.assert_called_with( + # mvn_output_file_path='/mock/mvn_output.txt' + # ) From f80fa11057cd10044d487f76e949b0f676a78948 Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 17:52:44 -0700 Subject: [PATCH 10/13] add testing stubs --- tests/step_implementers/deploy/test_helm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/step_implementers/deploy/test_helm.py b/tests/step_implementers/deploy/test_helm.py index 6ec3271f..8767fac1 100644 --- a/tests/step_implementers/deploy/test_helm.py +++ b/tests/step_implementers/deploy/test_helm.py @@ -93,9 +93,9 @@ def test_result(self): ] ) -@patch.object(Helm, '_run_maven_step') -@patch.object(Helm, 'write_working_file', return_value='/mock/mvn_output.txt') -class TestStepImplementerMavenPackage__run_step( +@patch.object(Helm, '_run_step') +@patch.object(Helm, 'write_working_file', return_value='/mock/helm_output.txt') +class TestStepImplementerDeployHelm__run_step( BaseStepImplementerTestCase ): def create_step_implementer( From 6e45052f2686dfac6d193ff06cce6385a1660e77 Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Tue, 10 Aug 2021 18:18:37 -0700 Subject: [PATCH 11/13] add testing stubs --- tests/step_implementers/deploy/test_helm.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/step_implementers/deploy/test_helm.py b/tests/step_implementers/deploy/test_helm.py index 8767fac1..e1605158 100644 --- a/tests/step_implementers/deploy/test_helm.py +++ b/tests/step_implementers/deploy/test_helm.py @@ -100,7 +100,10 @@ class TestStepImplementerDeployHelm__run_step( ): def create_step_implementer( self, - step_config={}, + step_config= { + 'helm-chart': 'mock-chart', + 'helm-release': 'mock-release' + }, workflow_result=None, parent_work_dir_path='' ): @@ -135,7 +138,7 @@ def create_step_implementer( # package_artifact_names = [ # f'my-app.jar' # ] - # def run_maven_side_effect(mvn_output_file_path): + # def run_helm_side_effect(helm_output_file_path): # os.makedirs(artifact_parent_dir, exist_ok=True) # for artifact_name in package_artifact_names: # artifact_path = os.path.join( @@ -151,14 +154,14 @@ def create_step_implementer( # # # create expected step result # expected_step_result = StepResult( - # step_name='package', - # sub_step_name='MavenPackage', - # sub_step_implementer_name='MavenPackage' + # step_name='deploy', + # sub_step_name='Helm', + # sub_step_implementer_name='Helm' # ) # expected_step_result.add_artifact( # description="Standard out and standard error from maven.", - # name='maven-output', - # value='/mock/mvn_output.txt' + # name='helm-output', + # value='/helm/mvn_output.txt' # ) # expected_step_result.add_artifact( # name='packages', @@ -174,8 +177,8 @@ def create_step_implementer( # ) # # mock_write_working_file.assert_called_once() - # mock_run_maven_step.assert_called_with( - # mvn_output_file_path='/mock/mvn_output.txt' + # mock_run_step.assert_called_with( + # helm_output_file_path='/mock/helm_output.txt' # ) # # def test_success_multiple_packaged_artifact( From 450b58ef5cf0470ee4878c57d5894e48bb56b6e7 Mon Sep 17 00:00:00 2001 From: Gerard Fulton Date: Wed, 11 Aug 2021 05:44:25 -0700 Subject: [PATCH 12/13] test stubs --- tests/step_implementers/deploy/test_helm.py | 49 +++++++++++---------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/tests/step_implementers/deploy/test_helm.py b/tests/step_implementers/deploy/test_helm.py index e1605158..49e0c77c 100644 --- a/tests/step_implementers/deploy/test_helm.py +++ b/tests/step_implementers/deploy/test_helm.py @@ -2,9 +2,10 @@ import re from io import IOBase -from unittest.mock import Mock, patch +from pathlib import Path +from unittest.mock import patch -from src.ploigos_step_runner.results.workflow_result import WorkflowResult +# from ploigos_step_runner import StepResult, StepRunnerException, WorkflowResult from tests.helpers.base_step_implementer_test_case import \ BaseStepImplementerTestCase from testfixtures import TempDirectory @@ -93,28 +94,28 @@ def test_result(self): ] ) -@patch.object(Helm, '_run_step') -@patch.object(Helm, 'write_working_file', return_value='/mock/helm_output.txt') -class TestStepImplementerDeployHelm__run_step( - BaseStepImplementerTestCase -): - def create_step_implementer( - self, - step_config= { - 'helm-chart': 'mock-chart', - 'helm-release': 'mock-release' - }, - workflow_result=None, - parent_work_dir_path='' - ): - return self.create_given_step_implementer( - step_implementer=Helm, - step_config=step_config, - step_name='deploy', - implementer='Helm', - workflow_result=workflow_result, - parent_work_dir_path=parent_work_dir_path - ) +# @patch.object(Helm, '_run_step') +# @patch.object(Helm, 'write_working_file', return_value='/mock/helm_output.txt') +# class TestStepImplementerDeployHelm__run_step( +# BaseStepImplementerTestCase +# ): +# def create_step_implementer( +# self, +# step_config= { +# 'helm-chart': 'mock-chart', +# 'helm-release': 'mock-release' +# }, +# workflow_result=None, +# parent_work_dir_path='' +# ): +# return self.create_given_step_implementer( +# step_implementer=Helm, +# step_config=step_config, +# step_name='deploy', +# implementer='Helm', +# workflow_result=workflow_result, +# parent_work_dir_path=parent_work_dir_path +# ) # def test_success_single_packaged_artifact( # self, From 0e8d29cc7d1d3c143bb6149e5c055aa2eabaaf33 Mon Sep 17 00:00:00 2001 From: gstewart Date: Tue, 5 Oct 2021 14:18:46 -0400 Subject: [PATCH 13/13] tweaks from review, unfinished --- .../step_implementers/deploy/helm.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/ploigos_step_runner/step_implementers/deploy/helm.py b/src/ploigos_step_runner/step_implementers/deploy/helm.py index 2796df26..7aa6c093 100644 --- a/src/ploigos_step_runner/step_implementers/deploy/helm.py +++ b/src/ploigos_step_runner/step_implementers/deploy/helm.py @@ -28,14 +28,18 @@ create_sh_redirect_to_multiple_streams_fn_callback DEFAULT_CONFIG = { - 'helm-flags': [] + 'helm-release-name': "", + 'helm-additional-arguments': [] } REQUIRED_CONFIG_OR_PREVIOUS_STEP_RESULT_ARTIFACT_KEYS = [ - 'helm-chart', - 'helm-release' + 'helm-chart' ] +DEFAULT_RELEASE_FORMAT = "{org}-{app}-{service}" +DEFAULT_CHART_FORMAT = "{service}" + + class Helm(StepImplementer): """`StepImplementer` for the `deploy` step using Helm. """ @@ -47,7 +51,6 @@ def __init__( # pylint: disable=too-many-arguments config, environment=None ): - super().__init__( workflow_result=workflow_result, parent_work_dir_path=parent_work_dir_path, @@ -68,6 +71,7 @@ def step_implementer_config_defaults(): ----- These are the lowest precedence configuration values. """ + return DEFAULT_CONFIG @staticmethod @@ -97,16 +101,21 @@ def _run_step(self): # pylint: disable=too-many-locals """ step_result = StepResult.from_step_implementer(self) + if not self.get_value("helm-release-name"): + self.__setattr__("helm-release-name", DEFAULT_RELEASE_FORMAT) + # TODO:: fill in placeholders in DEFAULT_RELEASE_FORMAT before setting + # install the helm chart helm_output_file_path = self.write_working_file('helm_output.txt') try: # execute helm step (params come from config) with open(helm_output_file_path, 'w') as helm_output_file: - sh.helm( # pylint: disable=no-member + # pylint: disable=no-member + sh.helm( 'secrets', 'upgrade', '--install', + self.get_value('helm-release-name'), self.get_value('helm-chart'), - self.get_value('helm-release'), - *self.get_value('helm-flags'), + *self.get_value('helm-additional-arguments'), _out=create_sh_redirect_to_multiple_streams_fn_callback([ sys.stdout, helm_output_file @@ -126,5 +135,6 @@ def _run_step(self): # pylint: disable=too-many-locals name='helm-output', value=helm_output_file_path ) + # TODO:: run helm test to ensure success return step_result