Skip to content

Commit

Permalink
Support terraform config generation from secret template
Browse files Browse the repository at this point in the history
  • Loading branch information
zzaakiirr committed Oct 10, 2024
1 parent 1a87e3e commit 152452a
Show file tree
Hide file tree
Showing 12 changed files with 788 additions and 5 deletions.
2 changes: 1 addition & 1 deletion lib/command/terraform/generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def generate_app_config(app)
generator = TerraformConfig::Generator.new(config: config, template: template)

# TODO: Delete line below after all template kinds are supported
next unless %w[gvc identity].include?(template["kind"])
next unless %w[gvc identity secret].include?(template["kind"])

File.write(terraform_app_dir.join(generator.filename), generator.tf_config.to_tf, mode: "a+")
end
Expand Down
4 changes: 3 additions & 1 deletion lib/core/terraform_config/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ def tf_value(value)

case value
when String
expression?(value) ? value : "\"#{value}\""
return value if expression?(value)

value.include?("\n") ? "EOF\n#{value.strip.indent(2)}\nEOF" : "\"#{value}\""
else
value
end
Expand Down
14 changes: 14 additions & 0 deletions lib/core/terraform_config/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def filename
case template["kind"]
when "gvc"
"gvc.tf"
when "secret"
"secrets.tf"
when "identity"
"identities.tf"
else
Expand All @@ -26,6 +28,8 @@ def tf_config
gvc_config
when "identity"
identity_config
when "secret"
secret_config
else
raise "Unsupported template kind - #{template['kind']}"
end
Expand Down Expand Up @@ -64,6 +68,16 @@ def identity_config
)
end

def secret_config
TerraformConfig::Secret.new(
name: template["name"],
description: template["description"],
type: template["type"],
data: template["data"],
tags: template["tags"]
)
end

def env
template.dig("spec", "env").to_h { |env_var| [env_var["name"], env_var["value"]] }
end
Expand Down
2 changes: 1 addition & 1 deletion lib/core/terraform_config/gvc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def initialize(
@locations = locations
@pull_secrets = pull_secrets
@env = env
@load_balancer = load_balancer&.transform_keys { |k| k.to_s.underscore.to_sym }
@load_balancer = load_balancer&.underscore_keys&.symbolize_keys
end
# rubocop:enable Metrics/ParameterLists

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

module TerraformConfig
class Secret < Base
attr_reader :name, :type, :data, :description, :tags

def initialize(name:, type:, data:, description: nil, tags: nil)
super()

@name = name
@type = type
@data = data.is_a?(Hash) ? data.underscore_keys.symbolize_keys : data
@description = description
@tags = tags
end

def to_tf
block :resource, :cpln_secret, name do
argument :name, name
argument :description, description, optional: true
argument :tags, tags, optional: true

secret_data
end
end

private

def secret_data
case type
when "azure-sdk", "dictionary", "docker", "gcp"
argument type.underscore, data, optional: true
when "azure-connector", "aws", "ecr", "keypair", "nats-account", "opaque", "tls", "userpass"
send("#{type.underscore}_tf")
else
raise "Invalid secret type given - #{type}"
end
end

def aws_tf
block :aws do
argument :secret_key, data.fetch(:secret_key)
argument :access_key, data.fetch(:access_key)
argument :role_arn, data.fetch(:role_arn, nil), optional: true
argument :external_id, data.fetch(:external_id, nil), optional: true
end
end

def azure_connector_tf
block :azure_connector do
argument :url, data.fetch(:url)
argument :code, data.fetch(:code)
end
end

def ecr_tf
block :ecr do
argument :secret_key, data.fetch(:secret_key)
argument :access_key, data.fetch(:access_key)
argument :repos, data.fetch(:repos)
argument :role_arn, data.fetch(:role_arn, nil), optional: true
argument :external_id, data.fetch(:external_id, nil), optional: true
end
end

def keypair_tf
block :keypair do
argument :secret_key, data.fetch(:secret_key)
argument :public_key, data.fetch(:public_key, nil), optional: true
argument :passphrase, data.fetch(:passphrase, nil), optional: true
end
end

def nats_account_tf
block :nats_account do
argument :account_id, data.fetch(:account_id)
argument :private_key, data.fetch(:private_key)
end
end

def opaque_tf
block :opaque do
argument :payload, data.fetch(:payload)
argument :encoding, data.fetch(:encoding, nil), optional: true
end
end

def tls_tf
block :tls do
argument :key, data.fetch(:key)
argument :cert, data.fetch(:cert)
argument :chain, data.fetch(:chain, nil), optional: true
end
end

def userpass_tf
block :userpass do
argument :username, data.fetch(:username)
argument :password, data.fetch(:password)
argument :encoding, data.fetch(:encoding, nil), optional: true
end
end
end
end
17 changes: 17 additions & 0 deletions lib/patches/hash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

class Hash
# Copied from Rails
def symbolize_keys
transform_keys { |key| key.to_sym rescue key } # rubocop:disable Style/RescueModifier
end

def underscore_keys
transform_keys do |key|
underscored = key.to_s.underscore
key.is_a?(Symbol) ? underscored.to_sym : underscored
rescue StandardError
key
end
end
end
3 changes: 2 additions & 1 deletion lib/patches/string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ def unindent
gsub(/^#{scan(/^[ \t]+(?=\S)/).min}/, "")
end

# Copied from Rails
def underscore
gsub(/(.)([A-Z])/, '\1_\2').downcase
gsub("::", "/").gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').tr("-", "_").downcase
end
end
# rubocop:enable Style/OptionalBooleanParameter, Lint/UnderscorePrefixedVariableName
2 changes: 1 addition & 1 deletion spec/command/terraform/generate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def common_config_files
end

def app_config_files
%w[gvc.tf identities.tf].map do |config_file_path|
%w[gvc.tf identities.tf secrets.tf].map do |config_file_path|
TERRAFORM_CONFIG_DIR_PATH.join(app, config_file_path)
end
end
Expand Down
34 changes: 34 additions & 0 deletions spec/core/terraform_config/generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@

let(:config) { instance_double(Config, org: "org-name", app: "app-name") }

context "when template's kind is unsupported" do
let(:template) { { "kind" => "invalid" } }

it "does not generate terraform config or filename for it", :aggregate_failures do
expect { generator.tf_config }.to raise_error("Unsupported template kind - #{template['kind']}")
expect { generator.filename }.to raise_error("Unsupported template kind - #{template['kind']}")
end
end

context "when template's kind is gvc" do
let(:template) do
{
Expand Down Expand Up @@ -89,4 +98,29 @@
expect(tf_filename).to eq("identities.tf")
end
end

context "when template's kind is secret" do
let(:template) do
{
"kind" => "secret",
"type" => "dictionary",
"name" => "secret-name",
"description" => "description",
"tags" => { "tag1" => "tag1_value", "tag2" => "tag2_value" },
"data" => { "key1" => "key1_value", "key2" => "key2_value2" }
}
end

it "generates correct terraform config and filename for it", :aggregate_failures do
tf_config = generator.tf_config
expect(tf_config).to be_an_instance_of(TerraformConfig::Secret)

expect(tf_config.name).to eq("secret-name")
expect(tf_config.description).to eq("description")
expect(tf_config.tags).to eq("tag1" => "tag1_value", "tag2" => "tag2_value")

tf_filename = generator.filename
expect(tf_filename).to eq("secrets.tf")
end
end
end
Loading

0 comments on commit 152452a

Please sign in to comment.