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

Add terraform import command #244

Merged
merged 5 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,14 @@ cpflow setup-app -a $APP_NAME
cpflow terraform generate
```

### `terraform import`

- Imports terraform resources from the generated configuration files

```sh
cpflow terraform import
```

### `version`

- Displays the current version of the CLI
Expand Down
2 changes: 1 addition & 1 deletion lib/command/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def self.all_commands # rubocop:disable Metrics/MethodLength
Dir["#{__dir__}/**/*.rb"].each_with_object({}) do |file, result|
content = File.read(file)

classname = content.match(/^\s+class (\w+) < (?:.*Base)(?:$| .*$)/)&.captures&.first
classname = content.match(/^\s+class (?!Base\b)(\w+) < (?:.*(?!Command::)Base)(?:$| .*$)/)&.captures&.first
next unless classname

namespaces = content.scan(/^\s+module (\w+)/).flatten
Expand Down
35 changes: 35 additions & 0 deletions lib/command/terraform/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Command
module Terraform
class Base < Command::Base
private

def templates
parser = TemplateParser.new(self)
template_files = Dir["#{parser.template_dir}/*.yml"]

if template_files.empty?
Shell.warn("No templates found in #{parser.template_dir}")
return []
end

parser.parse(template_files)
rescue StandardError => e
Shell.warn("Error parsing templates: #{e.message}")
[]
end

def terraform_dir
@terraform_dir ||= begin
full_path = config.options.fetch(:dir, Cpflow.root_path.join("terraform"))
Pathname.new(full_path).tap do |path|
FileUtils.mkdir_p(path)
rescue StandardError => e
Shell.abort("Invalid directory: #{e.message}")
end
end
end
end
end
end
26 changes: 0 additions & 26 deletions lib/command/terraform/generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,32 +94,6 @@ def clean_terraform_app_dir(terraform_app_dir)
FileUtils.rm_rf(terraform_app_dir.join(child))
end
end

def templates
parser = TemplateParser.new(self)
template_files = Dir["#{parser.template_dir}/*.yml"]

if template_files.empty?
Shell.warn("No templates found in #{parser.template_dir}")
return []
end

parser.parse(template_files)
rescue StandardError => e
Shell.warn("Error parsing templates: #{e.message}")
[]
end

def terraform_dir
@terraform_dir ||= begin
full_path = config.options.fetch(:dir, Cpflow.root_path.join("terraform"))
Pathname.new(full_path).tap do |path|
FileUtils.mkdir_p(path)
rescue StandardError => e
Shell.abort("Invalid directory: #{e.message}")
end
end
end
end
end
end
79 changes: 79 additions & 0 deletions lib/command/terraform/import.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

module Command
module Terraform
class Import < Base
SUBCOMMAND_NAME = "terraform"
NAME = "import"
OPTIONS = [
app_option,
dir_option
].freeze
DESCRIPTION = "Imports terraform resources"
LONG_DESCRIPTION = <<~DESC
- Imports terraform resources from the generated configuration files
DESC
WITH_INFO_HEADER = false

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

Dir.chdir(terraform_app_dir) do
run_terraform_init

resources.each do |resource|
run_terraform_import(resource[:address], resource[:id])
end
end
end
end

private

def run_terraform_init
result = Shell.cmd("terraform", "init", capture_stderr: true)

if result[:success]
Shell.info(result[:output])
else
Shell.abort("Failed to initialize terraform - #{result[:output]}")
end
end

def run_terraform_import(address, id)
result = Shell.cmd("terraform", "import", address, id, capture_stderr: true)
Shell.info(result[:output])
end

def resources
tf_configs.filter_map do |tf_config|
next unless tf_config.importable?

{ address: tf_config.reference, id: resource_id(tf_config) }
end
end

def tf_configs
templates.flat_map do |template|
zzaakiirr marked this conversation as resolved.
Show resolved Hide resolved
TerraformConfig::Generator.new(config: config, template: template).tf_configs.values
end
end

def resource_id(tf_config)
case tf_config
when TerraformConfig::Gvc, TerraformConfig::Policy,
TerraformConfig::Secret, TerraformConfig::Agent,
TerraformConfig::AuditContext
tf_config.name
else
"#{config.app}:#{tf_config.name}"
end
end

def terraform_app_dir
terraform_dir.join(config.app)
zzaakiirr marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
end
13 changes: 9 additions & 4 deletions lib/core/shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ class << self
attr_reader :tmp_stderr, :verbose
end

def self.shell
@shell ||= Thor::Shell::Color.new
end

def self.use_tmp_stderr
@tmp_stderr = Tempfile.create

Expand All @@ -35,6 +31,10 @@ def self.confirm(message)
shell.yes?("#{message} (y/N)")
end

def self.info(message)
shell.say(message)
end

def self.warn(message)
Kernel.warn(color("WARNING: #{message}", :yellow))
end
Expand Down Expand Up @@ -97,4 +97,9 @@ def self.trap_interrupt
exit(ExitCode::INTERRUPT)
end
end

def self.shell
@shell ||= Thor::Shell::Color.new
end
private_class_method :shell
end
8 changes: 8 additions & 0 deletions lib/core/terraform_config/agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ def initialize(name:, description: nil, tags: nil)
@tags = tags
end

def importable?
true
end

def reference
"cpln_agent.#{name}"
end

def to_tf
block :resource, :cpln_agent, name do
argument :name, name
Expand Down
8 changes: 8 additions & 0 deletions lib/core/terraform_config/audit_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ def initialize(name:, description: nil, tags: nil)
@tags = tags
end

def importable?
true
end

def reference
"cpln_audit_context.#{name}"
end

def to_tf
block :resource, :cpln_audit_context, name do
argument :name, name
Expand Down
8 changes: 8 additions & 0 deletions lib/core/terraform_config/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ module TerraformConfig
class Base
include Dsl

def importable?
false
end

def reference
raise NotImplementedError if importable?
end

def to_tf
raise NotImplementedError
end
Expand Down
8 changes: 8 additions & 0 deletions lib/core/terraform_config/gvc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ def initialize( # rubocop:disable Metrics/ParameterLists
@load_balancer = load_balancer&.deep_underscore_keys&.deep_symbolize_keys
end

def importable?
true
end

def reference
"cpln_gvc.#{name}"
end
zzaakiirr marked this conversation as resolved.
Show resolved Hide resolved

def to_tf
block :resource, :cpln_gvc, name do
argument :name, name
Expand Down
8 changes: 8 additions & 0 deletions lib/core/terraform_config/identity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ def initialize(gvc:, name:, description: nil, tags: nil)
@tags = tags
end

def importable?
true
end

def reference
"cpln_identity.#{name}"
end

def to_tf
block :resource, :cpln_identity, name do
argument :gvc, gvc
Expand Down
8 changes: 8 additions & 0 deletions lib/core/terraform_config/policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
@bindings = bindings&.map { |data| data.deep_underscore_keys.deep_symbolize_keys }
end

def importable?
true
end

def reference
"cpln_policy.#{name}"
end

def to_tf
block :resource, :cpln_policy, name do
argument :name, name
Expand Down
8 changes: 8 additions & 0 deletions lib/core/terraform_config/secret.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ def initialize(name:, type:, data:, description: nil, tags: nil)
@data = prepare_data(type: type, data: data)
end

def importable?
true
end

def reference
"cpln_secret.#{name}"
end

def to_tf
block :resource, :cpln_secret, name do
argument :name, name
Expand Down
8 changes: 8 additions & 0 deletions lib/core/terraform_config/volume_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
validate_attributes!
end

def importable?
true
end

def reference
"cpln_volume_set.#{name}"
end

def to_tf
block :resource, :cpln_volume_set, name do
base_arguments_tf
Expand Down
10 changes: 9 additions & 1 deletion lib/core/terraform_config/workload.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module TerraformConfig
class Workload < Base
class Workload < Base # rubocop:disable Metrics/ClassLength
RAW_ARGS = %i[
containers options local_options rollout_options security_options
firewall_spec load_balancer job
Expand Down Expand Up @@ -64,6 +64,14 @@ def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
@job = job
end

def importable?
true
end

def reference
"module.#{name}.cpln_workload.workload"
end
zzaakiirr marked this conversation as resolved.
Show resolved Hide resolved

def to_tf
block :module, name do
argument :source, "../workload"
Expand Down
1 change: 1 addition & 0 deletions lib/cpflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

# We need to require base before all commands, since the commands inherit from it
require_relative "command/base"
require_relative "command/terraform/base"
# We need to require base terraform config before all commands, since the terraform configs inherit from it
require_relative "core/terraform_config/base"

Expand Down
Loading