This repository has been archived by the owner on Feb 29, 2024. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge "Create or update deployment plan from git repository"
- Loading branch information
Showing
7 changed files
with
417 additions
and
99 deletions.
There are no files selected for viewing
13 changes: 13 additions & 0 deletions
13
releasenotes/notes/git-support-for-deployment-plans-cac4d3746689cbda.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
features: | ||
- | | ||
The create_deployment_plan workflow has been updated to provide support for | ||
creating a deployment plan from a git repository of heat templates. A tag | ||
or branch can be specified in the repo url with an '@'. | ||
Example: https://github.com/openstack/project.git@stable/newton | ||
deprecations: | ||
- | | ||
The tripleo.plan_management.v1.create_default_deployment_plan is deprecated | ||
and will be removed in the Queens release. The udpates to the | ||
tripleo.plan_management.v1.create_deployment_plan ensures that it provides | ||
the same functionality. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# Copyright 2017 Red Hat, Inc. | ||
# All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
# not use this file except in compliance with the License. You may obtain | ||
# a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations | ||
# under the License. | ||
import glob | ||
import logging | ||
import shutil | ||
import tempfile | ||
|
||
from git import Repo | ||
import six | ||
|
||
from mistral.actions import base | ||
from mistral.workflow import utils as mistral_workflow_utils | ||
|
||
LOG = logging.getLogger(__name__) | ||
|
||
|
||
class GitCloneAction(base.Action): | ||
"""Clones a remote git repository | ||
:param container: name of the container associated with the plan | ||
:param url: url of git repository | ||
:return: returns local path of cloned git repository | ||
""" | ||
|
||
def __init__(self, container, url): | ||
super(GitCloneAction, self).__init__() | ||
self.container = container | ||
self.url = url | ||
|
||
def _checkout_reference(self, repo, ref): | ||
return repo.git.checkout(repo.refs[ref]) | ||
|
||
def run(self): | ||
# make a temp directory to contain the repo | ||
local_dir_path = tempfile.mkdtemp( | ||
suffix="_%s_import" % self.container) | ||
url_bits = self.url.rsplit('@') | ||
err_msg = None | ||
try: | ||
# create a bare repo | ||
repo = Repo.clone_from(url_bits[0], local_dir_path) | ||
except Exception: | ||
err_msg = ("Error cloning remote repository: %s " % url_bits[0]) | ||
LOG.exception(err_msg) | ||
return mistral_workflow_utils.Result(error=err_msg) | ||
|
||
# if a tag value was given, checkout that tag | ||
if len(url_bits) > 1: | ||
try: | ||
self._checkout_reference(repo, url_bits[-1]) | ||
except IndexError: | ||
err_msg = ("Error finding %s reference " | ||
"from remote repository" % url_bits[-1]) | ||
LOG.exception(err_msg) | ||
except Exception: | ||
err_msg = ("Error checking out %s reference from remote " | ||
"repository %s" % (url_bits[-1], url_bits[0])) | ||
LOG.exception(err_msg) | ||
|
||
if err_msg: | ||
return mistral_workflow_utils.Result(error=err_msg) | ||
|
||
return local_dir_path | ||
|
||
|
||
class GitCleanupAction(base.Action): | ||
"""Removes temporary files associated with GitCloneAction operations | ||
:param container: name of the container associated with the plan | ||
:return: None if successful. Returns error on failure to delete | ||
associated temporary files | ||
""" | ||
def __init__(self, container): | ||
self.container = container | ||
|
||
def run(self): | ||
try: | ||
temp_dir = tempfile.gettempdir() | ||
target_path = '%s/*_%s_import' % (temp_dir, self.container) | ||
path = glob.glob(target_path)[0] | ||
shutil.rmtree(path) | ||
except IndexError as idx_err: | ||
LOG.exception("Directory not found: %s" % target_path) | ||
return mistral_workflow_utils.Result(error=six.text_type(idx_err)) | ||
except OSError as os_err: | ||
LOG.exception("Error removing directory: %s" % target_path) | ||
return mistral_workflow_utils.Result(error=six.text_type(os_err)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
# Copyright 2017 Red Hat, Inc. | ||
# All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
# not use this file except in compliance with the License. You may obtain | ||
# a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations | ||
# under the License. | ||
import mock | ||
import os | ||
import shutil | ||
import tempfile | ||
import uuid | ||
|
||
import git | ||
from mistral.workflow import utils as mistral_workflow_utils | ||
|
||
from tripleo_common.actions import vcs | ||
from tripleo_common.tests import base | ||
|
||
|
||
class GitCloneActionTest(base.TestCase): | ||
|
||
def setUp(self): | ||
super(GitCloneActionTest, self).setUp() | ||
|
||
self.temp_url = "/tmp/testdir" | ||
self.git_url = "https://github.com/openstack/tripleo-common.git" | ||
self.tag_ref = "some.test.ref" | ||
self.container = "overcloudtest" | ||
|
||
@mock.patch('tempfile.mkdtemp') | ||
@mock.patch('git.Repo.clone_from') | ||
def test_run(self, mock_repo_clone, mock_mkdtemp): | ||
|
||
mock_mkdtemp.return_value = self.temp_url | ||
action = vcs.GitCloneAction(self.container, self.git_url) | ||
action.run() | ||
|
||
mock_mkdtemp.assert_called() | ||
mock_repo_clone.assert_called_with(self.git_url, self.temp_url) | ||
|
||
@mock.patch('tempfile.mkdtemp') | ||
@mock.patch('git.Repo.clone_from') | ||
def test_run_repo_failure(self, mock_repo_clone, mock_mkdtemp): | ||
|
||
mock_mkdtemp.return_value = self.temp_url | ||
mock_repo_clone.side_effect = git.exc.GitCommandError | ||
action = vcs.GitCloneAction(self.container, self.git_url) | ||
result = action.run() | ||
|
||
expected = mistral_workflow_utils.Result( | ||
error="Error cloning remote repository: %s " % self.git_url | ||
) | ||
|
||
mock_mkdtemp.assert_called() | ||
mock_repo_clone.assert_called_with(self.git_url, self.temp_url) | ||
self.assertEqual(result, expected) | ||
|
||
@mock.patch('tempfile.mkdtemp') | ||
@mock.patch('git.Repo.clone_from') | ||
@mock.patch( | ||
'tripleo_common.actions.vcs.GitCloneAction._checkout_reference') | ||
def test_run_ref_not_found(self, mock_checkout, mock_repo_clone, | ||
mock_mkdtemp): | ||
|
||
mock_mkdtemp.return_value = self.temp_url | ||
mock_checkout.side_effect = IndexError | ||
action = vcs.GitCloneAction( | ||
self.container, | ||
"{url}@{tag}".format(url=self.git_url, tag=self.tag_ref) | ||
) | ||
result = action.run() | ||
|
||
err_msg = ("Error finding %s reference from remote repository" % | ||
self.tag_ref) | ||
|
||
expected = mistral_workflow_utils.Result(error=err_msg) | ||
|
||
self.assertEqual(result, expected, "Error messages don't match.") | ||
|
||
mock_mkdtemp.assert_called() | ||
mock_repo_clone.assert_called_with(self.git_url, self.temp_url) | ||
|
||
@mock.patch('tempfile.mkdtemp') | ||
@mock.patch('git.Repo.clone_from') | ||
@mock.patch( | ||
'tripleo_common.actions.vcs.GitCloneAction._checkout_reference') | ||
def test_run_ref_checkout_error(self, mock_checkout, mock_repo_clone, | ||
mock_mkdtemp): | ||
|
||
mock_mkdtemp.return_value = self.temp_url | ||
mock_checkout.side_effect = git.cmd.GitCommandError | ||
action = vcs.GitCloneAction( | ||
self.container, | ||
"{url}@{tag}".format(url=self.git_url, tag=self.tag_ref) | ||
) | ||
result = action.run() | ||
|
||
err_msg = ("Error checking out %s reference from remote " | ||
"repository %s" % (self.tag_ref, self.git_url)) | ||
|
||
expected = mistral_workflow_utils.Result(error=err_msg) | ||
|
||
self.assertEqual(result, expected, "Error messages don't match.") | ||
|
||
mock_mkdtemp.assert_called() | ||
mock_repo_clone.assert_called_with(self.git_url, self.temp_url) | ||
|
||
|
||
class GitCleanupActionTest(base.TestCase): | ||
|
||
def setUp(self): | ||
super(GitCleanupActionTest, self).setUp() | ||
self.container = "overcloud" | ||
self.temp_test_dir = tempfile.mkdtemp( | ||
suffix="_%s_import" % self.container) | ||
|
||
def tearDown(self): | ||
super(GitCleanupActionTest, self).tearDown() | ||
if os.path.exists(self.temp_test_dir): | ||
shutil.rmtree(self.temp_test_dir) | ||
|
||
def test_run(self): | ||
action = vcs.GitCleanupAction(self.container) | ||
action.run() | ||
self.assertFalse(os.path.exists(self.temp_test_dir)) | ||
|
||
def test_run_with_error(self): | ||
action = vcs.GitCleanupAction(str(uuid.uuid4())) | ||
result = action.run() | ||
self.assertIn("list index", str(result.error)) |
Oops, something went wrong.