Skip to content

Commit ea975dd

Browse files
committed
Add output command to Terraform.
1 parent 3368536 commit ea975dd

5 files changed

Lines changed: 259 additions & 45 deletions

File tree

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1-
from typing import Iterable
1+
from typing import IO, Iterable
22

33
from invoke.context import Context
44

55
import infrablocks.invoke_terraform.terraform as tf
6-
from infrablocks.invoke_terraform.terraform.terraform import Environment
6+
from infrablocks.invoke_terraform.terraform.terraform import (
7+
Environment,
8+
)
79

810

911
class InvokeExecutor(tf.Executor):
1012
def __init__(self, context: Context):
1113
self._context = context
1214

1315
def execute(
14-
self, command: Iterable[str], env: Environment | None = None
16+
self,
17+
command: Iterable[str],
18+
environment: Environment | None = None,
19+
stdout: IO[str] | None = None,
20+
stderr: IO[str] | None = None,
1521
) -> None:
16-
self._context.run(" ".join(command), env=(env or {}))
22+
self._context.run(
23+
" ".join(command),
24+
env=(environment if environment is not None else {}),
25+
out_stream=stdout,
26+
err_stream=stderr,
27+
)

src/infrablocks/invoke_terraform/terraform/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from .terraform import BackendConfig, ConfigurationValue, Variables
2+
from .terraform import Environment as Environment
23
from .terraform import Executor as Executor
34
from .terraform import Terraform as Terraform
45

56
__all__ = [
67
"BackendConfig",
78
"ConfigurationValue",
9+
"Environment",
810
"Executor",
911
"Terraform",
1012
"Variables",

src/infrablocks/invoke_terraform/terraform/terraform.py

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import json
22
from collections.abc import Mapping, Sequence
3+
from tempfile import TemporaryFile
4+
from typing import IO, Literal
35

46
type ConfigurationValue = (
57
bool
@@ -13,11 +15,37 @@
1315
type Variables = Mapping[str, ConfigurationValue]
1416
type BackendConfig = str | Mapping[str, ConfigurationValue]
1517
type Environment = Mapping[str, str]
18+
type StreamName = Literal["stdout"] | Literal["stderr"]
19+
type StreamNames = set[StreamName]
20+
21+
22+
class Result:
23+
def __init__(self, stdout: IO[str] | None, stderr: IO[str] | None):
24+
self.stdout = stdout
25+
self.stderr = stderr
1626

1727

1828
class Executor:
19-
def execute(self, command: Sequence[str], env: Environment | None) -> None:
20-
raise Exception("NotImplementedException")
29+
def execute(
30+
self,
31+
command: Sequence[str],
32+
environment: Environment | None = None,
33+
stdout: IO[str] | None = None,
34+
stderr: IO[str] | None = None,
35+
) -> None:
36+
raise NotImplementedError
37+
38+
39+
def _captures(capture: StreamNames | None, stream: StreamName) -> bool:
40+
return capture is not None and stream in capture
41+
42+
43+
def _capture_stream(
44+
capture: StreamNames | None, stream: StreamName
45+
) -> IO[str] | None:
46+
if _captures(capture, stream):
47+
return TemporaryFile(mode="w+t")
48+
return None
2149

2250

2351
class Terraform:
@@ -41,7 +69,7 @@ def init(
4169
if reconfigure:
4270
command = command + ["-reconfigure"]
4371

44-
self._executor.execute(command, env=environment)
72+
self._executor.execute(command, environment=environment)
4573

4674
def plan(
4775
self,
@@ -52,7 +80,7 @@ def plan(
5280
base_command = self._build_base_command(chdir)
5381
command = base_command + ["plan"] + self._build_vars(vars)
5482

55-
self._executor.execute(command, env=environment)
83+
self._executor.execute(command, environment=environment)
5684

5785
def apply(
5886
self,
@@ -70,7 +98,7 @@ def apply(
7098
+ self._build_vars(vars)
7199
)
72100

73-
self._executor.execute(command, env=environment)
101+
self._executor.execute(command, environment=environment)
74102

75103
def select_workspace(
76104
self,
@@ -87,7 +115,43 @@ def select_workspace(
87115

88116
command = command + [workspace]
89117

90-
self._executor.execute(command, env=environment)
118+
self._executor.execute(command, environment=environment)
119+
120+
def output(
121+
self,
122+
chdir: str | None = None,
123+
name: str | None = None,
124+
raw: bool = False,
125+
json: bool = False,
126+
environment: Environment | None = None,
127+
capture: StreamNames | None = None,
128+
) -> Result:
129+
base_command = self._build_base_command(chdir)
130+
command = base_command + ["output"]
131+
132+
if raw:
133+
command = command + ["-raw"]
134+
if json:
135+
command = command + ["-json"]
136+
if name is not None:
137+
command = command + [name]
138+
139+
stdout = _capture_stream(capture, "stdout")
140+
stderr = _capture_stream(capture, "stderr")
141+
142+
self._executor.execute(
143+
command,
144+
environment=environment,
145+
stdout=stdout,
146+
stderr=stderr,
147+
)
148+
149+
if stdout is not None:
150+
stdout.seek(0)
151+
if stderr is not None:
152+
stderr.seek(0)
153+
154+
return Result(stdout, stderr)
91155

92156
@staticmethod
93157
def _build_base_command(chdir: str | None) -> list[str]:

0 commit comments

Comments
 (0)