Skip to content

Commit

Permalink
0.18.16 build (#161)
Browse files Browse the repository at this point in the history
* RD-4693-Add-Import-Resource (#158)

* RD-4693-Add-Import-Resource

* RD-4693-Address-comments

* fix update version issue (#159)

* fix update version issue

* respond to comments

* flake8

* fix update source (#162)

* handle-empty-previous-state (#163)

Co-authored-by: Ahmad Musa <[email protected]>
  • Loading branch information
EarthmanT and ahmadiesa-abu authored Apr 19, 2022
1 parent 6f94259 commit 3987ad3
Show file tree
Hide file tree
Showing 8 changed files with 423 additions and 89 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
0.18.16:
- RD-4603 Update Terraform Binary Workflow
- RD-4693 Add import resource functionality.
0.18.15: RD-4023 Terratag
0.18.14: RD-4372 Make sure that terraform plan only uses module nodes.
0.18.13: Fix issue in tflint
Expand Down
121 changes: 104 additions & 17 deletions cloudify_tf/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from .decorators import (
with_terraform,
skip_if_existing)
from .terraform import Terraform
from .terraform.tools_base import TFToolException


Expand Down Expand Up @@ -155,13 +154,20 @@ def plan(ctx,
environment_variables)

if source or source_path:
with utils.update_terraform_source(source, source_path) as tf_src:
tf = Terraform.from_ctx(ctx, tf_src)
json_result, plain_text_result = _plan(tf)
else:
json_result, plain_text_result = _plan(tf)
tf.root_module = utils.update_terraform_source(source, source_path)
json_result, plain_text_result = _plan(tf)
ctx.instance.runtime_properties['plan'] = json_result
ctx.instance.runtime_properties['plain_text_plan'] = plain_text_result
resource_config = utils.get_resource_config()
resource_config.update(
{
'source': source,
'source_path': source_path
}
)
ctx.instance.runtime_properties['resource_config'] = resource_config
ctx.instance.runtime_properties['previous_tf_state_file'] = \
utils.get_terraform_state_file(tf.root_module)


@operation
Expand Down Expand Up @@ -293,14 +299,19 @@ def _reload_template(ctx,
source = utils.handle_previous_source_format(source)
if destroy_previous:
destroy(tf=tf, ctx=ctx)
with utils.update_terraform_source(source,
source_path) as terraform_source:
new_tf = Terraform.from_ctx(ctx, terraform_source)
old_plan = ctx.instance.runtime_properties.get('plan')
_apply(new_tf, old_plan, force)
ctx.instance.runtime_properties['resource_config'] = \
utils.get_resource_config()
_state_pull(new_tf)
tf.root_module = utils.update_terraform_source(source, source_path)
old_plan = ctx.instance.runtime_properties.get('plan')
_apply(tf, old_plan, force)
resource_config.update(
{
'source': source,
'source_path': source_path
}
)
ctx.instance.runtime_properties['resource_config'] = resource_config
_state_pull(tf)
ctx.instance.runtime_properties['previous_tf_state_file'] = \
utils.get_terraform_state_file(tf.root_module)


@operation
Expand Down Expand Up @@ -331,14 +342,15 @@ def reload_template(ctx,

@operation
@skip_if_existing
def install(ctx, **_):
def install(ctx, installation_source=None, **_):
installation_dir = get_node_instance_dir()
executable_path = utils.get_executable_path()
plugins = utils.get_plugins()
plugins_dir = utils.get_plugins_dir()
installation_source = utils.get_installation_source()
installation_source = \
installation_source or utils.get_installation_source()

if os.path.isfile(executable_path):
if os.path.isfile(executable_path) and ctx.workflow_id == 'install':
ctx.logger.info(
'Terraform executable already found at {path}; '
'skipping installation of executable'.format(
Expand Down Expand Up @@ -434,3 +446,78 @@ def set_directory_config(ctx, **_):
resource_plugins_dir
ctx.source.instance.runtime_properties['storage_path'] = \
resource_storage_dir


def _import_resource(ctx,
tf,
resource_id,
resource_address,
source=None,
source_path=None,
variables=None,
environment_variables=None,
**_):

_handle_new_vars(ctx.instance.runtime_properties,
tf,
variables,
environment_variables,
update=True)

if not all([resource_address, resource_id]):
raise NonRecoverableError(
"A new value for the following parameters must be provided:"
" resource_address, resource_id.")

resource_config = utils.get_resource_config()
if not source:
source = resource_config.get('source')
if not source_path:
source_path = resource_config.get('source_path')

source = utils.handle_previous_source_format(source)
tf.root_module = utils.update_terraform_source(source, source_path)
resource_config.update(
{
'source': source,
'source_path': source_path
}
)
ctx.instance.runtime_properties['resource_config'] = resource_config
ctx.instance.runtime_properties['previous_tf_state_file'] = \
utils.get_terraform_state_file(tf.root_module)
try:
tf.init()
tf.import_resource(resource_address, resource_id)
_state_pull(tf)
except Exception as ex:
_, _, tb = sys.exc_info()
raise NonRecoverableError(
"Failed executing terraform import. ",
causes=[exception_to_error_cause(ex, tb)])


@operation
@with_terraform
def import_resource(ctx,
tf,
resource_id,
resource_address,
source=None,
source_path=None,
variables=None,
environment_variables=None,
**kwargs):
"""
Terraform import resource given resource_address and resource_id as inputs
"""

_import_resource(ctx,
tf,
resource_id,
resource_address,
source,
source_path,
variables,
environment_variables,
**kwargs)
21 changes: 20 additions & 1 deletion cloudify_tf/terraform/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def __init__(self,

self.binary_path = binary_path
self.plugins_dir = self.set_plugins_dir(plugins_dir)
self.root_module = root_module
self._root_module = root_module
self.logger = logger
self.additional_args = additional_args
self._version = version
Expand Down Expand Up @@ -94,6 +94,15 @@ def __init__(self,
self._variables = variables
self.provider_upgrade = provider_upgrade

@property
def root_module(self):
return self._root_module

@root_module.setter
def root_module(self, value):
self._root_module = value
utils.try_to_copy_old_state_file(value)

@property
def flags(self):
if not self._flags:
Expand Down Expand Up @@ -399,6 +408,13 @@ def show_state(self, resource_name, plan_file_path=None):
command = self._tf_command(options)
return self.execute(command)

def import_resource(self, resource_address, resource_id):
options = ['import', '-no-color']
with self.runtime_file(options):
options.extend([resource_address, resource_id])
command = self._tf_command(options)
return self.execute(command)

@staticmethod
def from_ctx(ctx, terraform_source, skip_tf=False):
try:
Expand Down Expand Up @@ -467,6 +483,9 @@ def run_terratag(self):
self.terratag.validate()
self.terratag.terraform_root_module = self.root_module
commands = []
if os.path.dirname(self.binary_path) not in os.environ['PATH']:
os.environ['PATH'] = '{}:{}'.format(
os.environ['PATH'], os.path.dirname(self.binary_path))
with self.runtime_file(commands):
self.terratag.terratag()

Expand Down
3 changes: 2 additions & 1 deletion cloudify_tf/terraform/terratag.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
# 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 json

import re
import json
from os import path

from cloudify_common_sdk.utils import install_binary
Expand Down
57 changes: 57 additions & 0 deletions cloudify_tf/tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
install,
check_drift,
setup_linters,
import_resource,
set_directory_config)
from ..utils import RELATIONSHIP_INSTANCE
from ..terraform import Terraform
Expand Down Expand Up @@ -483,3 +484,59 @@ def test_check_drift(self, *_):
'The cloudify.nodes.terraform.Module node instance {} '
'has no drifts.'.format(ctx.instance.id)
)

@patch('cloudify_tf.utils._unzip_archive')
@patch('cloudify_tf.utils.get_terraform_state_file', return_value=False)
@patch('cloudify_tf.utils.get_cloudify_version', return_value="6.1.0")
@patch('cloudify_tf.utils.get_node_instance_dir',
return_value=test_dir3)
@patch('cloudify_tf.terraform.Terraform.terraform_outdated',
return_value=False)
def test_import_resource(self, *_):
conf = self.get_terraform_module_conf_props(test_dir3)
ctx = self.mock_ctx("test_import_resource", conf)
current_ctx.set(ctx=ctx)
kwargs = {
'ctx': ctx,
'resource_address': 'aws_instance.example_vm',
'resource_id': 'i-06e504391884deb3c'
}
mock_plan_and_show = {
"format_version": "0.1",
"terraform_version": "0.13.4",
"variables": {},
"planned_values": {},
"resource_changes": [],
"prior_state": {},
"configuration": {}
}

tf_pulled_resources = {
'resources': [{
'name': 'example_vm',
'value': {
"mode": "managed",
"type": "aws_instance",
"name": "example_vm",
}
}
]
}
tf_output = {}
mock_tf_import = Mock()
mock_tf_import.init.return_value = 'terraform initialized folder'
mock_tf_import.plan.return_value = 'terraform plan'
mock_tf_import.import_resource.return_value = 'Import successful!'
mock_tf_import.state_pull.return_value = tf_pulled_resources
mock_tf_import.output.return_value = tf_output
mock_tf_import.plan_and_show.return_value = mock_plan_and_show

with patch('cloudify_tf.terraform.Terraform.from_ctx',
return_value=mock_tf_import):
import_resource(**kwargs)
self.assertTrue(mock_tf_import.import_resource.called)
self.assertEqual(
ctx.instance.runtime_properties['resources'],
{'example_vm': tf_pulled_resources.get('resources')[0]})
self.assertEqual(ctx.instance.runtime_properties['outputs'],
tf_output)
Loading

0 comments on commit 3987ad3

Please sign in to comment.