Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture terraform errors from deploy command, use scrolling buffer #1868

Merged
merged 34 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
387cf86
capture terraform errors and outputs
araghukas Nov 29, 2023
d4e7b3d
correct leading whitespace
araghukas Nov 29, 2023
fe620d6
update changelog
araghukas Nov 29, 2023
2fc6587
ignore "No state file .." message
araghukas Nov 29, 2023
8e97871
revise exit return codes
araghukas Nov 29, 2023
3d8405b
Merge branch 'develop' into capture-terraform-errors
araghukas Nov 29, 2023
872fd5e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 29, 2023
937e027
update tests
araghukas Nov 29, 2023
edad590
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 29, 2023
6f3daf7
add tests for coverage
araghukas Nov 29, 2023
6ced594
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 29, 2023
b36bb69
check success case in tf version test
araghukas Nov 29, 2023
d4ab18e
update tests
araghukas Nov 29, 2023
6e4ef99
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 29, 2023
b1f44f4
pre-commit fixes
araghukas Nov 29, 2023
8a0e0ed
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 29, 2023
57c8f74
modify to pass without `terraform` in CI test env
araghukas Nov 29, 2023
b69479e
retry tests fix
araghukas Nov 29, 2023
e35d482
retry retry fix
araghukas Nov 29, 2023
c1fcbc8
remove useless pragma tags
araghukas Nov 29, 2023
0fea752
use exist status 1 for error
araghukas Nov 29, 2023
b268186
include crm.up test with 'valid' options
araghukas Nov 29, 2023
0d23504
correct exit code
araghukas Nov 29, 2023
67f222d
update test
araghukas Nov 29, 2023
fe39226
new test for up command with 'valid' options
araghukas Nov 29, 2023
a576a67
update to work without terraform install
araghukas Nov 29, 2023
7ea1b4b
more changes for CI tests
araghukas Nov 29, 2023
388616e
complete coverage for test_tf_version_error
araghukas Nov 29, 2023
be21e4c
rename to avoid module vs. function conflict
araghukas Nov 29, 2023
2d00ecc
add comment
araghukas Nov 29, 2023
a0e5aea
add tests for deploy up, down, status
araghukas Nov 29, 2023
ead6555
fix typo
araghukas Nov 29, 2023
8f69448
fix paths since rename
araghukas Nov 29, 2023
5b27d5d
use importlib for right path of -e install (awsbatch issue)
araghukas Nov 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [UNRELEASED]

### Changed

- Terraform output to use scrolling buffer.
- Terraform output handling to show errors.

## [0.231.0-rc.0] - 2023-11-28

### Authors

- Ara Ghukasyan <[email protected]>
- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>


### Added

- check for `/bin/bash` AND `/bin/sh` (in that order) to execute bash leptons
Expand Down
121 changes: 67 additions & 54 deletions covalent/cloud_resource_manager/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ def get_plugin_settings(
for key, value in executor_options.items():
try:
settings_dict[key]["value"] = value
except:
except Exception:
logger.error(f"No such option '{key}'. Use --help for available options")
sys.exit()
sys.exit(1)

return settings_dict

Expand Down Expand Up @@ -164,10 +164,9 @@ def __init__(
self._terraform_log_env_vars = {
"TF_LOG": "ERROR",
"TF_LOG_PATH": os.path.join(self.executor_tf_path, "terraform-error.log"),
"PATH": "$PATH:/usr/bin",
}

def _print_stdout(self, process: subprocess.Popen, print_callback: Callable) -> int:
def _poll_process(self, process: subprocess.Popen, print_callback: Callable) -> int:
"""
Print the stdout from the subprocess to console

Expand All @@ -179,12 +178,10 @@ def _print_stdout(self, process: subprocess.Popen, print_callback: Callable) ->
Return code of the process.

"""
while (retcode := process.poll()) is None:
if (proc_stdout := process.stdout.readline()) and print_callback:
print_callback(proc_stdout.strip().decode("utf-8"))
return retcode

# TODO: Return the command output along with return code
while (returncode := process.poll()) is None:
if print_callback:
print_callback(process.stdout.readline())
return returncode

def _parse_terraform_error_log(self) -> List[str]:
"""Parse the terraform error logs.
Expand Down Expand Up @@ -235,16 +232,21 @@ def _get_resource_status(
Returns:
status: str - status of plugin
"""
_, stderr = proc.communicate()

cmds = cmd.split(" ")
tfstate_path = cmds[-1].split("=")[-1]
if stderr is None:
return self._terraform_error_validator(tfstate_path=tfstate_path)
else:
raise subprocess.CalledProcessError(
returncode=1, cmd=cmd, stderr=self._parse_terraform_error_log()

returncode = self._poll_process(proc, print_callback=None)
stderr = proc.stderr.read()
if returncode != 0 and "No state file was found!" not in stderr:
print(
"Unable to get resource status due to the following error:\n\n",
stderr,
file=sys.stderr,
)

return self._terraform_error_validator(tfstate_path=tfstate_path)

def _log_error_msg(self, cmd) -> None:
"""
Log error msg with valid command to terraform-erro.log
Expand All @@ -261,7 +263,6 @@ def _log_error_msg(self, cmd) -> None:
def _run_in_subprocess(
self,
cmd: str,
workdir: str,
env_vars: Optional[Dict[str, str]] = None,
print_callback: Optional[Callable] = None,
) -> Union[None, str]:
Expand All @@ -270,39 +271,50 @@ def _run_in_subprocess(

Args:
cmd: Command to execute in the subprocess
workdir: Working directory of the subprocess
env_vars: Dictionary of environment variables to set in the processes execution environment

Returns:
Union[None, str]
- For 'covalent deploy status'
returns status of the deplyment
returns status of the deployment
- Others
return None
"""
if git := shutil.which("git"):
proc = subprocess.Popen(
args=cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=workdir,
shell=True,
env=env_vars,
)
TERRAFORM_STATE = "state list -state"
if TERRAFORM_STATE in cmd:
return self._get_resource_status(proc=proc, cmd=cmd)
retcode = self._print_stdout(proc, print_callback)

if retcode != 0:
self._log_error_msg(cmd=cmd)
raise subprocess.CalledProcessError(
returncode=retcode, cmd=cmd, stderr=self._parse_terraform_error_log()
)
else:
if not shutil.which("git"):
self._log_error_msg(cmd=cmd)
logger.error("Git not found on the system.")
sys.exit()
sys.exit(1)

env_vars = env_vars or {}
env_vars.update({"PATH": os.environ["PATH"]})

proc = subprocess.Popen(
args=cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=self.executor_tf_path,
universal_newlines=True,
shell=True,
env=env_vars,
)

if "state list -state" in cmd:
return self._get_resource_status(proc=proc, cmd=cmd)

returncode = self._poll_process(proc, print_callback)

if returncode != 0:
self._log_error_msg(cmd=cmd)

_, stderr = proc.communicate()
raise subprocess.CalledProcessError(
returncode=returncode,
cmd=cmd,
stderr=self._parse_terraform_error_log(),
output=stderr,
)

return None

def _update_config(self, tf_executor_config_file: str) -> None:
"""
Expand Down Expand Up @@ -348,18 +360,22 @@ def _get_tf_path(self) -> str:
"""
if terraform := shutil.which("terraform"):
result = subprocess.run(
["terraform --version"], shell=True, capture_output=True, text=True
["terraform --version"],
shell=True,
capture_output=True,
text=True,
check=True,
)
version = result.stdout.split("v", 1)[1][:3]
if float(version) < 1.4:
logger.error(
"Old version of terraform found. Please update it to version greater than 1.3"
)
sys.exit()
sys.exit(1)
return terraform
else:
logger.error("Terraform not found on system")
exit()

logger.error("Terraform not found on system")
sys.exit(1)

def _get_tf_statefile_path(self) -> str:
"""
Expand Down Expand Up @@ -401,14 +417,16 @@ def up(self, print_callback: Callable, dry_run: bool = True) -> None:

# Run `terraform init`
self._run_in_subprocess(
cmd=tf_init, workdir=self.executor_tf_path, env_vars=self._terraform_log_env_vars
cmd=tf_init,
env_vars=self._terraform_log_env_vars,
print_callback=print_callback,
)

# Setup terraform infra variables as passed by the user
tf_vars_env_dict = os.environ.copy()

if self.executor_options:
with open(tfvars_file, "w") as f:
with open(tfvars_file, "w", encoding="utf-8") as f:
for key, value in self.executor_options.items():
tf_vars_env_dict[f"TF_VAR_{key}"] = value

Expand All @@ -418,17 +436,15 @@ def up(self, print_callback: Callable, dry_run: bool = True) -> None:
# Run `terraform plan`
self._run_in_subprocess(
cmd=tf_plan,
workdir=self.executor_tf_path,
env_vars=self._terraform_log_env_vars,
print_callback=print_callback,
)

# Create infrastructure as per the plan
# Run `terraform apply`
if not dry_run:
cmd_output = self._run_in_subprocess(
self._run_in_subprocess(
cmd=tf_apply,
workdir=self.executor_tf_path,
env_vars=tf_vars_env_dict.update(self._terraform_log_env_vars),
print_callback=print_callback,
)
Expand Down Expand Up @@ -472,7 +488,6 @@ def down(self, print_callback: Callable) -> None:
# Run `terraform destroy`
self._run_in_subprocess(
cmd=tf_destroy,
workdir=self.executor_tf_path,
print_callback=print_callback,
env_vars=self._terraform_log_env_vars,
)
Expand Down Expand Up @@ -510,6 +525,4 @@ def status(self) -> None:
tf_state = " ".join([terraform, "state", "list", f"-state={tf_state_file}"])

# Run `terraform state list`
return self._run_in_subprocess(
cmd=tf_state, workdir=self.executor_tf_path, env_vars=self._terraform_log_env_vars
)
return self._run_in_subprocess(cmd=tf_state, env_vars=self._terraform_log_env_vars)
4 changes: 2 additions & 2 deletions covalent_dispatcher/_cli/groups/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
# 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.
from .db import db
from .deploy import deploy
from .db_group import db
from .deploy_group import deploy
Loading
Loading