Merge branch 'use-semantic-version--string-instead-of-float' into 'master'
[breaking change] use semantic version string See merge request domaindrivenarchitecture/python-terraform!2
This commit is contained in:
commit
71c544bad8
3 changed files with 29 additions and 27 deletions
|
@ -5,6 +5,7 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union
|
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union
|
||||||
|
from packaging import version
|
||||||
|
|
||||||
from dda_python_terraform.tfstate import Tfstate
|
from dda_python_terraform.tfstate import Tfstate
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ class Terraform:
|
||||||
var_file: Optional[str] = None,
|
var_file: Optional[str] = None,
|
||||||
terraform_bin_path: Optional[str] = None,
|
terraform_bin_path: Optional[str] = None,
|
||||||
is_env_vars_included: bool = True,
|
is_env_vars_included: bool = True,
|
||||||
terraform_version: Optional[float] = 0.13
|
terraform_semantic_version: Optional[str] = "0.13.0"
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
:param working_dir: the folder of the working folder, if not given,
|
:param working_dir: the folder of the working folder, if not given,
|
||||||
|
@ -69,6 +70,7 @@ class Terraform:
|
||||||
:param terraform_bin_path: binary path of terraform
|
:param terraform_bin_path: binary path of terraform
|
||||||
:type is_env_vars_included: bool
|
:type is_env_vars_included: bool
|
||||||
:param is_env_vars_included: included env variables when calling terraform cmd
|
:param is_env_vars_included: included env variables when calling terraform cmd
|
||||||
|
:param terrform_semantic_version encodes major.minor.patch version of terraform. Defaults to 0.13.0
|
||||||
"""
|
"""
|
||||||
self.is_env_vars_included = is_env_vars_included
|
self.is_env_vars_included = is_env_vars_included
|
||||||
self.working_dir = working_dir
|
self.working_dir = working_dir
|
||||||
|
@ -79,7 +81,7 @@ class Terraform:
|
||||||
self.terraform_bin_path = (
|
self.terraform_bin_path = (
|
||||||
terraform_bin_path if terraform_bin_path else "terraform"
|
terraform_bin_path if terraform_bin_path else "terraform"
|
||||||
)
|
)
|
||||||
self.terraform_version = terraform_version
|
self.terraform_semantic_version = terraform_semantic_version
|
||||||
self.var_file = var_file
|
self.var_file = var_file
|
||||||
self.temp_var_files = VariableFiles()
|
self.temp_var_files = VariableFiles()
|
||||||
|
|
||||||
|
@ -156,7 +158,7 @@ class Terraform:
|
||||||
global_opts = self._generate_default_general_options(dir_or_plan)
|
global_opts = self._generate_default_general_options(dir_or_plan)
|
||||||
default = kwargs.copy()
|
default = kwargs.copy()
|
||||||
# force is no longer a flag in version >= 1.0
|
# force is no longer a flag in version >= 1.0
|
||||||
if self.terraform_version < 1.0:
|
if version.parse(self.terraform_semantic_version) < version.parse("1.0.0"):
|
||||||
default["force"] = force
|
default["force"] = force
|
||||||
default["auto-approve"] = True
|
default["auto-approve"] = True
|
||||||
options = self._generate_default_options(default)
|
options = self._generate_default_options(default)
|
||||||
|
@ -458,13 +460,13 @@ class Terraform:
|
||||||
)
|
)
|
||||||
|
|
||||||
def _generate_default_args(self, dir_or_plan: Optional[str]) -> Sequence[str]:
|
def _generate_default_args(self, dir_or_plan: Optional[str]) -> Sequence[str]:
|
||||||
if (self.terraform_version < 1.0 and dir_or_plan):
|
if (version.parse(self.terraform_semantic_version) < version.parse("1.0.0") and dir_or_plan):
|
||||||
return [dir_or_plan]
|
return [dir_or_plan]
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _generate_default_general_options(self, dir_or_plan: Optional[str]) -> Dict[str, Any]:
|
def _generate_default_general_options(self, dir_or_plan: Optional[str]) -> Dict[str, Any]:
|
||||||
if (self.terraform_version >= 1.0 and dir_or_plan):
|
if (version.parse(self.terraform_semantic_version) >= version.parse("1.0.0") and dir_or_plan):
|
||||||
return {"chdir": dir_or_plan}
|
return {"chdir": dir_or_plan}
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
@ -544,7 +546,6 @@ class Terraform:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class VariableFiles:
|
class VariableFiles:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.files = []
|
self.files = []
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
packaging
|
|
@ -6,6 +6,7 @@ import shutil
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
from packaging import version
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.logging import LogCaptureFixture, caplog
|
from _pytest.logging import LogCaptureFixture, caplog
|
||||||
|
@ -17,8 +18,7 @@ root_logger = logging.getLogger()
|
||||||
|
|
||||||
current_path = os.path.dirname(os.path.realpath(__file__))
|
current_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
version = 1.0 if (os.environ.get("TFVER") and os.environ.get(
|
semantic_version = os.environ.get("TFVER")
|
||||||
"TFVER").startswith("1")) else 0.13
|
|
||||||
|
|
||||||
FILE_PATH_WITH_SPACE_AND_SPACIAL_CHARS = "test 'test.out!"
|
FILE_PATH_WITH_SPACE_AND_SPACIAL_CHARS = "test 'test.out!"
|
||||||
STRING_CASES = [
|
STRING_CASES = [
|
||||||
|
@ -60,7 +60,7 @@ CMD_CASES_0_x = [
|
||||||
var={"test_var": "test"},
|
var={"test_var": "test"},
|
||||||
raise_on_error=False,
|
raise_on_error=False,
|
||||||
),
|
),
|
||||||
# Expected output varies by terraform version
|
# Expected output varies by terraform semantic_version
|
||||||
"Plan: 0 to add, 0 to change, 0 to destroy.",
|
"Plan: 0 to add, 0 to change, 0 to destroy.",
|
||||||
0,
|
0,
|
||||||
False,
|
False,
|
||||||
|
@ -131,7 +131,7 @@ CMD_CASES_1_x = [
|
||||||
var={"test_var": "test"},
|
var={"test_var": "test"},
|
||||||
raise_on_error=False,
|
raise_on_error=False,
|
||||||
),
|
),
|
||||||
# Expected output varies by terraform version
|
# Expected output varies by terraform semantic_version
|
||||||
"Changes to Outputs:",
|
"Changes to Outputs:",
|
||||||
0,
|
0,
|
||||||
False,
|
False,
|
||||||
|
@ -236,7 +236,7 @@ def workspace_setup_teardown():
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def wrapper(workspace_name, create=True, delete=True, *args, **kwargs):
|
def wrapper(workspace_name, create=True, delete=True, *args, **kwargs):
|
||||||
tf = Terraform(working_dir=current_path, terraform_version=version)
|
tf = Terraform(working_dir=current_path, terraform_semantic_version=semantic_version)
|
||||||
tf.init()
|
tf.init()
|
||||||
if create:
|
if create:
|
||||||
tf.create_workspace(workspace_name, *args, **kwargs)
|
tf.create_workspace(workspace_name, *args, **kwargs)
|
||||||
|
@ -271,14 +271,14 @@ class TestTerraform:
|
||||||
|
|
||||||
@pytest.mark.parametrize(["method", "expected"], STRING_CASES)
|
@pytest.mark.parametrize(["method", "expected"], STRING_CASES)
|
||||||
def test_generate_cmd_string(self, method: Callable[..., str], expected: str):
|
def test_generate_cmd_string(self, method: Callable[..., str], expected: str):
|
||||||
tf = Terraform(working_dir=current_path, terraform_version=version)
|
tf = Terraform(working_dir=current_path, terraform_semantic_version=semantic_version)
|
||||||
result = method(tf)
|
result = method(tf)
|
||||||
|
|
||||||
strs = expected.split()
|
strs = expected.split()
|
||||||
for s in strs:
|
for s in strs:
|
||||||
assert s in result
|
assert s in result
|
||||||
|
|
||||||
@pytest.mark.parametrize(*(CMD_CASES_1_x if version >= 1.0 else CMD_CASES_0_x))
|
@pytest.mark.parametrize(*(CMD_CASES_1_x if version.parse(semantic_version) >= version.parse("1.0.0") else CMD_CASES_0_x))
|
||||||
def test_cmd(
|
def test_cmd(
|
||||||
self,
|
self,
|
||||||
method: Callable[..., str],
|
method: Callable[..., str],
|
||||||
|
@ -290,7 +290,7 @@ class TestTerraform:
|
||||||
folder: str,
|
folder: str,
|
||||||
):
|
):
|
||||||
with caplog.at_level(logging.INFO):
|
with caplog.at_level(logging.INFO):
|
||||||
tf = Terraform(working_dir=current_path, terraform_version=version)
|
tf = Terraform(working_dir=current_path, terraform_semantic_version=semantic_version)
|
||||||
tf.init(folder)
|
tf.init(folder)
|
||||||
try:
|
try:
|
||||||
ret, out, _ = method(tf)
|
ret, out, _ = method(tf)
|
||||||
|
@ -305,10 +305,10 @@ class TestTerraform:
|
||||||
assert expected_logs in caplog.text
|
assert expected_logs in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(*(APPLY_CASES_1_x if version >= 1.0 else APPLY_CASES_0_x))
|
@pytest.mark.parametrize(*(APPLY_CASES_1_x if version.parse(semantic_version) >= version.parse("1.0.0") else APPLY_CASES_0_x))
|
||||||
def test_apply(self, folder, variables, var_files, expected_output, options):
|
def test_apply(self, folder, variables, var_files, expected_output, options):
|
||||||
tf = Terraform(
|
tf = Terraform(
|
||||||
working_dir=current_path, variables=variables, var_file=var_files, terraform_version=version
|
working_dir=current_path, variables=variables, var_file=var_files, terraform_semantic_version=semantic_version
|
||||||
)
|
)
|
||||||
tf.init(folder)
|
tf.init(folder)
|
||||||
ret, out, err = tf.apply(folder, **options)
|
ret, out, err = tf.apply(folder, **options)
|
||||||
|
@ -318,7 +318,7 @@ class TestTerraform:
|
||||||
|
|
||||||
def test_apply_with_var_file(self, caplog: LogCaptureFixture):
|
def test_apply_with_var_file(self, caplog: LogCaptureFixture):
|
||||||
with caplog.at_level(logging.INFO):
|
with caplog.at_level(logging.INFO):
|
||||||
tf = Terraform(working_dir=current_path, terraform_version=version)
|
tf = Terraform(working_dir=current_path, terraform_semantic_version=semantic_version)
|
||||||
folder = "var_to_output"
|
folder = "var_to_output"
|
||||||
tf.init(folder)
|
tf.init(folder)
|
||||||
tf.apply(
|
tf.apply(
|
||||||
|
@ -338,7 +338,7 @@ class TestTerraform:
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_options(self, cmd, args, options, fmt_test_file):
|
def test_options(self, cmd, args, options, fmt_test_file):
|
||||||
tf = Terraform(working_dir=current_path, terraform_version=version)
|
tf = Terraform(working_dir=current_path, terraform_semantic_version=semantic_version)
|
||||||
ret, out, err = getattr(tf, cmd)(*args, **options)
|
ret, out, err = getattr(tf, cmd)(*args, **options)
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert out == ""
|
assert out == ""
|
||||||
|
@ -346,26 +346,26 @@ class TestTerraform:
|
||||||
def test_state_data(self):
|
def test_state_data(self):
|
||||||
cwd = os.path.join(current_path, "test_tfstate_file")
|
cwd = os.path.join(current_path, "test_tfstate_file")
|
||||||
tf = Terraform(working_dir=cwd, state="tfstate.test",
|
tf = Terraform(working_dir=cwd, state="tfstate.test",
|
||||||
terraform_version=version)
|
terraform_semantic_version=semantic_version)
|
||||||
tf.read_state_file()
|
tf.read_state_file()
|
||||||
assert tf.tfstate.modules[0]["path"] == ["root"]
|
assert tf.tfstate.modules[0]["path"] == ["root"]
|
||||||
|
|
||||||
def test_state_default(self):
|
def test_state_default(self):
|
||||||
cwd = os.path.join(current_path, "test_tfstate_file2")
|
cwd = os.path.join(current_path, "test_tfstate_file2")
|
||||||
tf = Terraform(working_dir=cwd, terraform_version=version)
|
tf = Terraform(working_dir=cwd, terraform_semantic_version=semantic_version)
|
||||||
tf.read_state_file()
|
tf.read_state_file()
|
||||||
assert tf.tfstate.modules[0]["path"] == ["default"]
|
assert tf.tfstate.modules[0]["path"] == ["default"]
|
||||||
|
|
||||||
def test_state_default_backend(self):
|
def test_state_default_backend(self):
|
||||||
cwd = os.path.join(current_path, "test_tfstate_file3")
|
cwd = os.path.join(current_path, "test_tfstate_file3")
|
||||||
tf = Terraform(working_dir=cwd, terraform_version=version)
|
tf = Terraform(working_dir=cwd, terraform_semantic_version=semantic_version)
|
||||||
tf.read_state_file()
|
tf.read_state_file()
|
||||||
assert tf.tfstate.modules[0]["path"] == ["default_backend"]
|
assert tf.tfstate.modules[0]["path"] == ["default_backend"]
|
||||||
|
|
||||||
def test_pre_load_state_data(self):
|
def test_pre_load_state_data(self):
|
||||||
cwd = os.path.join(current_path, "test_tfstate_file")
|
cwd = os.path.join(current_path, "test_tfstate_file")
|
||||||
tf = Terraform(working_dir=cwd, state="tfstate.test",
|
tf = Terraform(working_dir=cwd, state="tfstate.test",
|
||||||
terraform_version=version)
|
terraform_semantic_version=semantic_version)
|
||||||
assert tf.tfstate.modules[0]["path"] == ["root"]
|
assert tf.tfstate.modules[0]["path"] == ["root"]
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -373,7 +373,7 @@ class TestTerraform:
|
||||||
)
|
)
|
||||||
def test_override_default(self, folder, variables):
|
def test_override_default(self, folder, variables):
|
||||||
tf = Terraform(working_dir=current_path,
|
tf = Terraform(working_dir=current_path,
|
||||||
variables=variables, terraform_version=version)
|
variables=variables, terraform_semantic_version=semantic_version)
|
||||||
tf.init(folder)
|
tf.init(folder)
|
||||||
ret, out, err = tf.apply(
|
ret, out, err = tf.apply(
|
||||||
folder, var={"test_var": "test2"}, no_color=IsNotFlagged,
|
folder, var={"test_var": "test2"}, no_color=IsNotFlagged,
|
||||||
|
@ -389,7 +389,7 @@ class TestTerraform:
|
||||||
required_output = "test_output"
|
required_output = "test_output"
|
||||||
with caplog.at_level(logging.INFO):
|
with caplog.at_level(logging.INFO):
|
||||||
tf = Terraform(
|
tf = Terraform(
|
||||||
working_dir=current_path, variables={"test_var": expected_value}, terraform_version=version
|
working_dir=current_path, variables={"test_var": expected_value}, terraform_semantic_version=semantic_version
|
||||||
)
|
)
|
||||||
tf.init("var_to_output")
|
tf.init("var_to_output")
|
||||||
tf.apply("var_to_output")
|
tf.apply("var_to_output")
|
||||||
|
@ -403,7 +403,7 @@ class TestTerraform:
|
||||||
|
|
||||||
def test_destroy(self):
|
def test_destroy(self):
|
||||||
tf = Terraform(working_dir=current_path, variables={
|
tf = Terraform(working_dir=current_path, variables={
|
||||||
"test_var": "test"}, terraform_version=version)
|
"test_var": "test"}, terraform_semantic_version=semantic_version)
|
||||||
tf.init("var_to_output")
|
tf.init("var_to_output")
|
||||||
ret, out, err = tf.destroy("var_to_output")
|
ret, out, err = tf.destroy("var_to_output")
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
|
@ -414,7 +414,7 @@ class TestTerraform:
|
||||||
)
|
)
|
||||||
def test_plan(self, plan, variables, expected_ret):
|
def test_plan(self, plan, variables, expected_ret):
|
||||||
tf = Terraform(working_dir=current_path,
|
tf = Terraform(working_dir=current_path,
|
||||||
variables=variables, terraform_version=version)
|
variables=variables, terraform_semantic_version=semantic_version)
|
||||||
tf.init(plan)
|
tf.init(plan)
|
||||||
with pytest.raises(TerraformCommandError) as e:
|
with pytest.raises(TerraformCommandError) as e:
|
||||||
tf.plan(plan)
|
tf.plan(plan)
|
||||||
|
@ -424,7 +424,7 @@ class TestTerraform:
|
||||||
|
|
||||||
def test_fmt(self, fmt_test_file):
|
def test_fmt(self, fmt_test_file):
|
||||||
tf = Terraform(working_dir=current_path, variables={
|
tf = Terraform(working_dir=current_path, variables={
|
||||||
"test_var": "test"}, terraform_version=version)
|
"test_var": "test"}, terraform_semantic_version=semantic_version)
|
||||||
ret, out, err = tf.fmt(diff=True)
|
ret, out, err = tf.fmt(diff=True)
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue