Skip to content

Commit

Permalink
Put providers.tf & required_providers.tf to each app folder (#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
zzaakiirr authored Oct 23, 2024
1 parent 442554b commit 288046a
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 63 deletions.
46 changes: 29 additions & 17 deletions lib/command/terraform/generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,52 @@ class Generate < Base
WITH_INFO_HEADER = false

def call
generate_common_configs
generate_app_configs
end

private

def generate_common_configs
cpln_provider = TerraformConfig::RequiredProvider.new(
"cpln",
source: "controlplane-com/cpln",
version: "~> 1.0"
)

File.write(terraform_dir.join("providers.tf"), cpln_provider.to_tf)
end

def generate_app_configs
Array(config.app || config.apps.keys).each do |app|
config.instance_variable_set(:@app, app.to_s)
generate_app_config
end
end

private

def generate_app_config
terraform_app_dir = recreate_terraform_app_dir

generate_provider_configs(terraform_app_dir)

templates.each do |template|
generator = TerraformConfig::Generator.new(config: config, template: template)
File.write(terraform_app_dir.join(generator.filename), generator.tf_config.to_tf, mode: "a+")
rescue TerraformConfig::Generator::InvalidTemplateError => e
Shell.warn(e.message)
rescue StandardError => e
Shell.warn("Failed to generate config file from '#{template['kind']}' template: #{e.message}")
end
end

def generate_provider_configs(terraform_app_dir)
generate_required_providers(terraform_app_dir)
generate_providers(terraform_app_dir)
rescue StandardError => e
Shell.abort("Failed to generate provider config files: #{e.message}")
end

def generate_required_providers(terraform_app_dir)
required_cpln_provider = TerraformConfig::RequiredProvider.new(
name: "cpln",
org: config.org,
source: "controlplane-com/cpln",
version: "~> 1.0"
)

File.write(terraform_app_dir.join("required_providers.tf"), required_cpln_provider.to_tf)
end

def generate_providers(terraform_app_dir)
cpln_provider = TerraformConfig::Provider.new(name: "cpln", org: config.org)
File.write(terraform_app_dir.join("providers.tf"), cpln_provider.to_tf)
end

def recreate_terraform_app_dir
full_path = terraform_dir.join(config.app)

Expand Down
22 changes: 22 additions & 0 deletions lib/core/terraform_config/provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

module TerraformConfig
class Provider < Base
attr_reader :name, :options

def initialize(name:, **options)
super()

@name = name
@options = options
end

def to_tf
block :provider, name do
options.each do |option, value|
argument option, value
end
end
end
end
end
9 changes: 7 additions & 2 deletions lib/core/terraform_config/required_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

module TerraformConfig
class RequiredProvider < Base
attr_reader :name, :options
attr_reader :name, :org, :options

def initialize(name, **options)
def initialize(name:, org:, **options)
super()

@name = name
@org = org
@options = options
end

def to_tf
block :terraform do
block :cloud do
argument :organization, org
end

block :required_providers do
argument name, options
end
Expand Down
113 changes: 70 additions & 43 deletions spec/command/terraform/generate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,32 @@
FileUtils.rm_rf GENERATOR_PLAYGROUND_PATH
end

it "generates terraform config files", :aggregate_failures do
config_file_paths.each { |config_file_path| expect(config_file_path).not_to exist }
shared_examples "generates terraform config files" do
specify do
all_config_paths.each { |path| expect(path).not_to exist }

expect(result[:status]).to eq(0)
expect(result[:status]).to eq(ExitCode::SUCCESS)
expect(result[:stderr]).to err_msg ? include(err_msg) : be_empty

expect(config_file_paths).to all(exist)
expect(expected_config_paths).to all(exist)
(all_config_paths - expected_config_paths).each { |path| expect(path).not_to exist }
end
end

shared_examples "does not generate any terraform config files" do |err_msg|
it "fails with an error" do
all_config_paths.each { |path| expect(path).not_to exist }

expect(result[:status]).to eq(ExitCode::ERROR_DEFAULT)
expect(result[:stderr]).to include(err_msg)

all_config_paths.each { |path| expect(path).not_to exist }
end
end

it_behaves_like "generates terraform config files" do
let(:expected_config_paths) { all_config_paths }
let(:err_msg) { "Unsupported template kind: workload" }
end

context "when templates folder is empty" do
Expand All @@ -39,13 +59,9 @@
allow_any_instance_of(TemplateParser).to receive(:template_dir).and_return(template_dir) # rubocop:disable RSpec/AnyInstance
end

it "generates only common config files" do
config_file_paths.each { |config_file_path| expect(config_file_path).not_to exist }

expect(result[:stderr]).to include("No templates found in #{template_dir}")

expect(common_config_files).to all(exist)
app_config_files.each { |config_file_path| expect(config_file_path).not_to exist }
it_behaves_like "generates terraform config files" do
let(:expected_config_paths) { provider_config_paths }
let(:err_msg) { "No templates found in #{template_dir}" }
end
end

Expand All @@ -54,35 +70,49 @@
allow_any_instance_of(TemplateParser).to receive(:parse).and_raise("error") # rubocop:disable RSpec/AnyInstance
end

it "generates only common config files" do
config_file_paths.each { |config_file_path| expect(config_file_path).not_to exist }

expect(result[:stderr]).to include("Error parsing templates: error")

expect(common_config_files).to all(exist)
app_config_files.each { |config_file_path| expect(config_file_path).not_to exist }
it_behaves_like "generates terraform config files" do
let(:expected_config_paths) { provider_config_paths }
let(:err_msg) { "Error parsing templates: error" }
end
end

context "when --dir option is outside of project dir" do
let(:options) { ["-a", app, "--dir", GEM_TEMP_PATH.join("path-outside-of-project").to_s] }

it "aborts command execution" do
expect(result[:status]).to eq(ExitCode::ERROR_DEFAULT)
expect(result[:stderr]).to include(
"Directory to save terraform configuration files cannot be outside of current directory"
)
end
it_behaves_like "does not generate any terraform config files",
"Directory to save terraform configuration files cannot be outside of current directory"
end

context "when terraform config directory creation fails" do
before do
allow(FileUtils).to receive(:mkdir_p).and_raise("error")
end

it "aborts command execution" do
expect(result[:status]).to eq(ExitCode::ERROR_DEFAULT)
expect(result[:stderr]).to include("error")
it_behaves_like "does not generate any terraform config files", "Invalid directory: error"
end

context "when required provider config generation fails" do
let(:required_provider_config_stub) { instance_double(TerraformConfig::RequiredProvider) }

before do
allow(TerraformConfig::RequiredProvider).to receive(:new).and_return(required_provider_config_stub)
allow(required_provider_config_stub).to receive(:to_tf).and_raise("error")
end

it_behaves_like "does not generate any terraform config files", "Failed to generate provider config files"
end

context "when terraform config from template generation fails" do
let(:gvc_config_stub) { instance_double(TerraformConfig::Gvc) }

before do
allow(TerraformConfig::Gvc).to receive(:new).and_return(gvc_config_stub)
allow(gvc_config_stub).to receive(:to_tf).and_raise("error")
end

it_behaves_like "generates terraform config files" do
let(:expected_config_paths) { all_config_paths - [config_path("gvc.tf")] }
let(:err_msg) { "Failed to generate config file from 'gvc' template: error" }
end
end

Expand All @@ -93,28 +123,25 @@
)
end

it "generates common config files and warns about invalid template" do
config_file_paths.each { |config_file_path| expect(config_file_path).not_to exist }

expect(result[:status]).to eq(0)
expect(result[:stderr]).to include("Invalid template: error message")

expect(common_config_files).to all(exist)
app_config_files.each { |config_file_path| expect(config_file_path).not_to exist }
it_behaves_like "generates terraform config files" do
let(:expected_config_paths) { provider_config_paths }
let(:err_msg) { "Invalid template: error message" }
end
end

def config_file_paths
common_config_files + app_config_files
def all_config_paths
provider_config_paths + template_config_paths
end

def common_config_files
[TERRAFORM_CONFIG_DIR_PATH.join("providers.tf")]
def provider_config_paths
%w[required_providers.tf providers.tf].map { |filename| config_path(filename) }
end

def app_config_files
%w[gvc.tf identities.tf secrets.tf policies.tf volumesets.tf].map do |config_file_path|
TERRAFORM_CONFIG_DIR_PATH.join(app, config_file_path)
end
def template_config_paths
%w[gvc.tf identities.tf secrets.tf policies.tf volumesets.tf].map { |filename| config_path(filename) }
end

def config_path(name)
TERRAFORM_CONFIG_DIR_PATH.join(app, name)
end
end
26 changes: 26 additions & 0 deletions spec/core/terraform_config/provider_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require "spec_helper"

describe TerraformConfig::Provider do
let(:config) { described_class.new(name: name, **options) }

describe "#to_tf" do
subject(:generated) { config.to_tf }

context "when provider is cpln" do
let(:name) { "cpln" }
let(:options) { { org: "test-org" } }

it "generates correct config" do
expect(generated).to eq(
<<~EXPECTED
provider "cpln" {
org = "test-org"
}
EXPECTED
)
end
end
end
end
6 changes: 5 additions & 1 deletion spec/core/terraform_config/required_provider_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
require "spec_helper"

describe TerraformConfig::RequiredProvider do
let(:config) { described_class.new(name, **options) }
let(:config) { described_class.new(name: name, org: org, **options) }

describe "#to_tf" do
subject(:generated) { config.to_tf }

context "when provider is cpln" do
let(:name) { "cpln" }
let(:org) { "test-org" }
let(:options) { { source: "controlplane-com/cpln", version: "~> 1.0" } }

it "generates correct config" do
expect(generated).to eq(
<<~EXPECTED
terraform {
cloud {
organization = "test-org"
}
required_providers {
cpln = {
source = "controlplane-com/cpln"
Expand Down

0 comments on commit 288046a

Please sign in to comment.