Created
December 18, 2024 19:47
-
-
Save danelowe/03d0cc71efc2a844791f941c446efdf1 to your computer and use it in GitHub Desktop.
Terraform Module Directories
This file contains hidden or 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
class Command | |
def call(&block) | |
execute(&block) | |
end | |
private | |
def command | |
raise NotImplementedError | |
end | |
def spawn_options | |
{} | |
end | |
def execute(&block) | |
cmd = command | |
opts = spawn_options | |
opts[:chdir] = chdir if chdir | |
in_r, in_w = IO.pipe | |
opts[:in] = in_r | |
in_w.sync = true | |
out_r, out_w = IO.pipe | |
opts[:out] = out_w | |
err_r, err_w = IO.pipe | |
opts[:err] = err_w | |
child_io = [in_r, out_w, err_w] | |
parent_io = [in_w, out_r, err_r] | |
pid = spawn(*cmd, opts) | |
wait_thr = Process.detach(pid) | |
child_io.each(&:close) | |
begin | |
result = block&.call(Result.new(stdin: in_w, stdout: out_r, stderr: err_r, thread: wait_thr)) | |
raise(err_r.read || "non-zero exit code") unless wait_thr.value.success? | |
ensure | |
parent_io.each(&:close) | |
wait_thr.join | |
end | |
result | |
end | |
class Result < Struct.new(:stdin, :stdout, :stderr, :thread) | |
end | |
end |
This file contains hidden or 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
require "json" | |
require_relative "../lib/command" | |
class Workspace | |
ENVIRONMENTS = %w[production staging] | |
def initialize(env:, name:) | |
raise ArgumentError, "Invalid workspace name: #{name}" unless /^[a-z0-9_-]+$/.match?(name) | |
raise ArgumentError, "Invalid environment: #{env}" unless ENVIRONMENTS.include?(env) | |
@env = env | |
@name = name | |
end | |
attr_reader :env, :name | |
def module_directories | |
unless @module_directories | |
TerraformCommand.new(%w[init -input=false -get-plugins=false], workspace_path: path).call | |
modules_output = TerraformCommand.new(%w[modules -json], workspace_path: path).call { |result| JSON.parse(result.stdout.read) } | |
modules = modules_output["modules"].each_with_object({}) do |mod, acc| | |
acc[mod["key"]] = mod["source"] | |
end | |
@module_directories = [path] + modules.keys.map { |k| resolve_path(k, modules) } | |
end | |
@module_directories | |
end | |
def path | |
"infrastructure/#{@env}/#{@name}" | |
end | |
private | |
def resolve_path(module_name, modules) | |
paths = resolve_path_parts(module_name, modules) | |
absolute_path = File.realpath(File.join(path, *paths)) | |
absolute_pwd = File.realpath(".") | |
raise "Invalid path #{absolute_path}" unless absolute_path.start_with?(absolute_pwd) | |
Pathname.new(absolute_path).relative_path_from(absolute_pwd).to_s | |
end | |
def resolve_path_parts(module_name, modules) | |
rest, tail = module_name.split(/\.([^.]*)/) | |
if tail | |
[*resolve_path_parts(rest, modules), modules[module_name]] | |
else | |
[modules[module_name]] | |
end | |
end | |
class TerraformCommand < Command | |
def initialize(args, workspace_path:) | |
raise ArgumentError, "args must be an array" unless args.is_a?(Array) | |
raise ArgumentError, "Invalid workspace path: #{workspace_path}" unless %r{^[a-z0-9_\-\/]+$}.match?(workspace_path) | |
@args = args | |
@path = workspace_path | |
end | |
def command | |
["terraform", *@args] | |
end | |
def chdir | |
@path | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment