terraform-exporter 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 92a92c3eade1b46a2190e83dcf95cff3c1982b3c
4
+ data.tar.gz: 069d6299c8f979325efeda89a1c824436be210ba
5
+ SHA512:
6
+ metadata.gz: dc96559168d75c3ed04c4e18ffa68d0420d582c722e75f872fe7c8db3d4e950463213765de3a017cafe055b40579181d05ad3b9920506551c7277e12d33db9e8
7
+ data.tar.gz: 0f74ea201905c4a2424f7b966c377c0877f9d70de5282595e2addda9f39a4ecbb9f29f624ddf4f88d3ebcfe2f387807dc200338074518af846a31f0bf2be0fc6
@@ -0,0 +1,2 @@
1
+ pkg/
2
+ coverage/
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in terraformer.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem "guard"
8
+ gem "guard-rspec"
9
+
10
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.0")
11
+ gem "listen", "~> 3.1.0"
12
+ else
13
+ gem "listen", "< 3.1.0"
14
+ end
15
+
16
+ gem "rubocop"
17
+
18
+ gem "terminal-notifier-guard"
19
+ end
@@ -0,0 +1,127 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ terraform-exporter (0.0.1)
5
+ aws-sdk (~> 2.5.1)
6
+ dogapi (~> 1.23)
7
+ oj (~> 2.17.1)
8
+ ox (~> 2.4.0)
9
+ thor
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ ast (2.3.0)
15
+ aws-sdk (2.5.11)
16
+ aws-sdk-resources (= 2.5.11)
17
+ aws-sdk-core (2.5.11)
18
+ jmespath (~> 1.0)
19
+ aws-sdk-resources (2.5.11)
20
+ aws-sdk-core (= 2.5.11)
21
+ coderay (1.1.1)
22
+ coveralls (0.8.13)
23
+ json (~> 1.8)
24
+ simplecov (~> 0.11.0)
25
+ term-ansicolor (~> 1.3)
26
+ thor (~> 0.19.1)
27
+ tins (~> 1.6.0)
28
+ diff-lcs (1.2.5)
29
+ docile (1.1.5)
30
+ dogapi (1.23.0)
31
+ multi_json
32
+ ffi (1.9.14)
33
+ formatador (0.2.5)
34
+ guard (2.14.0)
35
+ formatador (>= 0.2.4)
36
+ listen (>= 2.7, < 4.0)
37
+ lumberjack (~> 1.0)
38
+ nenv (~> 0.1)
39
+ notiffany (~> 0.0)
40
+ pry (>= 0.9.12)
41
+ shellany (~> 0.0)
42
+ thor (>= 0.18.1)
43
+ guard-compat (1.2.1)
44
+ guard-rspec (4.7.3)
45
+ guard (~> 2.1)
46
+ guard-compat (~> 1.1)
47
+ rspec (>= 2.99.0, < 4.0)
48
+ jmespath (1.3.1)
49
+ json (1.8.3)
50
+ listen (3.1.5)
51
+ rb-fsevent (~> 0.9, >= 0.9.4)
52
+ rb-inotify (~> 0.9, >= 0.9.7)
53
+ ruby_dep (~> 1.2)
54
+ lumberjack (1.0.10)
55
+ method_source (0.8.2)
56
+ multi_json (1.12.1)
57
+ nenv (0.3.0)
58
+ notiffany (0.1.1)
59
+ nenv (~> 0.1)
60
+ shellany (~> 0.0)
61
+ oj (2.17.4)
62
+ ox (2.4.5)
63
+ parser (2.3.1.4)
64
+ ast (~> 2.2)
65
+ powerpack (0.1.1)
66
+ pry (0.10.4)
67
+ coderay (~> 1.1.0)
68
+ method_source (~> 0.8.1)
69
+ slop (~> 3.4)
70
+ rainbow (2.1.0)
71
+ rake (11.3.0)
72
+ rb-fsevent (0.9.7)
73
+ rb-inotify (0.9.7)
74
+ ffi (>= 0.5.0)
75
+ rspec (3.5.0)
76
+ rspec-core (~> 3.5.0)
77
+ rspec-expectations (~> 3.5.0)
78
+ rspec-mocks (~> 3.5.0)
79
+ rspec-core (3.5.3)
80
+ rspec-support (~> 3.5.0)
81
+ rspec-expectations (3.5.0)
82
+ diff-lcs (>= 1.2.0, < 2.0)
83
+ rspec-support (~> 3.5.0)
84
+ rspec-mocks (3.5.0)
85
+ diff-lcs (>= 1.2.0, < 2.0)
86
+ rspec-support (~> 3.5.0)
87
+ rspec-support (3.5.0)
88
+ rubocop (0.43.0)
89
+ parser (>= 2.3.1.1, < 3.0)
90
+ powerpack (~> 0.1)
91
+ rainbow (>= 1.99.1, < 3.0)
92
+ ruby-progressbar (~> 1.7)
93
+ unicode-display_width (~> 1.0, >= 1.0.1)
94
+ ruby-progressbar (1.8.1)
95
+ ruby_dep (1.4.0)
96
+ shellany (0.0.1)
97
+ simplecov (0.11.2)
98
+ docile (~> 1.1.0)
99
+ json (~> 1.8)
100
+ simplecov-html (~> 0.10.0)
101
+ simplecov-html (0.10.0)
102
+ slop (3.6.0)
103
+ term-ansicolor (1.3.2)
104
+ tins (~> 1.0)
105
+ terminal-notifier-guard (1.7.0)
106
+ thor (0.19.1)
107
+ tins (1.6.0)
108
+ unicode-display_width (1.1.1)
109
+
110
+ PLATFORMS
111
+ ruby
112
+
113
+ DEPENDENCIES
114
+ bundler (~> 1.7)
115
+ coveralls (~> 0.8.13)
116
+ guard
117
+ guard-rspec
118
+ listen (~> 3.1.0)
119
+ rake
120
+ rspec (~> 3.2)
121
+ rubocop
122
+ simplecov (~> 0.11.1)
123
+ terminal-notifier-guard
124
+ terraform-exporter!
125
+
126
+ BUNDLED WITH
127
+ 1.13.2
@@ -0,0 +1,12 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ rspec = dsl.rspec
6
+ watch(rspec.spec_helper) { rspec.spec_dir }
7
+ watch(rspec.spec_support) { rspec.spec_dir }
8
+ watch(rspec.spec_files)
9
+
10
+ ruby = dsl.ruby
11
+ dsl.watch_spec_files_for(ruby.lib_files)
12
+ end
@@ -0,0 +1,69 @@
1
+ # Terraformer
2
+
3
+ Is a fork of the [original package](https://github.com/dtan4/terraforming) that allows you to export resources to [Terraform](https://terraform.io/) style (tf, tfstate)
4
+
5
+ - [Supported version](#supported-version)
6
+ - [Installation](#installation)
7
+ - [Prerequisites](#prerequisites)
8
+ - [Usage](#usage)
9
+ - [Development](#development)
10
+ - [Contributing](#contributing)
11
+ - [License](#license)
12
+
13
+ ## Supported version
14
+
15
+ Ruby 2.1 or higher
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ $ gem install terraform-exporter
21
+ ```
22
+
23
+ ## Prerequisites
24
+
25
+ ### AWS credentials.
26
+
27
+ ```bash
28
+ export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
29
+ export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
30
+ export AWS_REGION=xx-yyyy-0
31
+ ```
32
+
33
+ You can also pass them as options `--aws_access_key_id` `--aws_secret_access_key` `--aws_region`
34
+
35
+ ### Datadog credentials
36
+
37
+ ```bash
38
+ export DATADOG_API_KEY=XXXXXXXXXXXXXXXXXXXX
39
+ export DATADOG_APP_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
40
+ ```
41
+
42
+ You can also pass them as options `--datadog_api_key` `--datadog_app_key`
43
+
44
+ ## Usage
45
+
46
+ ```bash
47
+ $ terraformer
48
+ Commands:
49
+ terraformer cwa # CloudWatch Alarms
50
+ terraformer dm # Datadog Metrics
51
+ terraformer help [COMMAND] # Describe available commands or one specific command
52
+
53
+ Options:
54
+ [--merge=MERGE] # tfstate file to merge
55
+ [--name=NAME] # name of the tf file
56
+ [--aws_access_key_id=AWS_ACCESS_KEY_ID] # Aws access key
57
+ [--aws_secret_access_key=AWS_SECRET_ACCESS_KEY] # Aws access key
58
+ [--aws_region=AWS_REGION] # Aws region
59
+ [--datadog_api_key=DATADOG_API_KEY] # Datadog api key
60
+ [--datadog_app_key=DATADOG_APP_KEY] # Datadog app key
61
+ ```
62
+
63
+ ## Contributing
64
+
65
+ 1. Fork it ( https://github.com/intercom/terraformer/fork )
66
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
67
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
68
+ 4. Push to the branch (`git push origin my-new-feature`)
69
+ 5. Create a new Pull Request
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "terraforming"
4
+ begin
5
+ Terraformer::CLI.start(ARGV)
6
+ rescue Terraformer::UserInputError, Terraformer::UserInputMaxAttemptsError, Terraformer::TerraformInvalidTfstateFile, Terraformer::TerraformFileNotFound => e
7
+ if e.class.is_a? Terraformer::UserInputError
8
+ exit 0
9
+ end
10
+ exit 1
11
+ end
@@ -0,0 +1,29 @@
1
+ require "oj"
2
+
3
+ begin
4
+ require "ox"
5
+ rescue NameError => e
6
+ spec = Gem::Specification.stubs.find { |s| s.name == 'ox' }
7
+ raise e unless spec
8
+ require File.join(spec.gem_dir, "lib/ox")
9
+ end
10
+
11
+ require "aws-sdk-core"
12
+ require 'dogapi'
13
+ require "erb"
14
+ require "json"
15
+ require "thor"
16
+ require "zlib"
17
+ require "securerandom"
18
+
19
+ require "terraformer/version"
20
+ require "terraformer/terraform"
21
+ require "terraformer/templating"
22
+ require "terraformer/user_input"
23
+ require "terraformer/normalizer"
24
+
25
+ require "terraformer/cli"
26
+ require "terraformer/credentials/aws"
27
+ require "terraformer/credentials/datadog"
28
+ require "terraformer/resource/cloud_watch_alarm"
29
+ require "terraformer/resource/datadog_monitor"
@@ -0,0 +1,41 @@
1
+ module Terraformer
2
+ class CLI < Thor
3
+ class_option :merge, type: :string, desc: "tfstate file to merge"
4
+ class_option :name, type: :string, desc: "name of the tf file"
5
+ class_option :aws_access_key_id, type: :string, desc: "aws access key"
6
+ class_option :aws_secret_access_key, type: :string, desc: "aws access key"
7
+ class_option :aws_region, type: :string, desc: "aws region"
8
+ class_option :datadog_api_key, type: :string, desc: "datadog api key"
9
+ class_option :datadog_app_key, type: :string, desc: "datadog app key"
10
+
11
+ desc "cwa", "CloudWatch Alarm"
12
+ def cwa
13
+ execute(Terraformer::Resource::CloudWatchAlarm, options)
14
+ end
15
+
16
+ desc "dm", "Datadog Monitor"
17
+ def dm
18
+ execute(Terraformer::Resource::DatadogMonitor, options)
19
+ end
20
+
21
+ def symbolize(obj)
22
+ return obj.reduce({}) do |memo, (k, v)|
23
+ memo.tap { |m| m[k.to_sym] = symbolize(v) }
24
+ end if obj.is_a? Hash
25
+
26
+ return obj.reduce([]) do |memo, v|
27
+ memo << symbolize(v); memo
28
+ end if obj.is_a? Array
29
+
30
+ obj
31
+ end
32
+
33
+ def execute(klass, options)
34
+ klass_name = klass.name
35
+ options = symbolize(options)
36
+ response = klass.execute(options)
37
+
38
+ Terraformer::Terraform.execute(response, klass_name, options)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ module Terraformer
2
+ module Credentials
3
+ class Aws
4
+ def self.get_from_options(options)
5
+ credentials = {}
6
+ %w(aws_region aws_access_key_id aws_secret_access_key).each do |key|
7
+ aws_key = key.sub("aws_", "").to_sym
8
+ credentials[aws_key] = options[key.to_sym].nil? ? options[key] : options[key.to_sym]
9
+ credentials[aws_key] = ENV[key.upcase] if credentials[aws_key].nil?
10
+ end
11
+
12
+ credentials.reject{ |k, v| v.nil? }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Terraformer
2
+ module Credentials
3
+ class Datadog
4
+ def self.get_from_options(options)
5
+ credentials = {}
6
+ %w(datadog_api_key datadog_app_key).each do |key|
7
+ datadog_key = key.sub("datadog_", "").to_sym
8
+ credentials[datadog_key] = options[key.to_sym].nil? ? options[key] : options[key.to_sym]
9
+ credentials[datadog_key] = ENV[key.upcase] if credentials[datadog_key].nil?
10
+ end
11
+
12
+ credentials.reject{ |k, v| v.nil? }
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,9 @@
1
+ module Terraformer
2
+ class Normalizer
3
+ class << self
4
+ def module_name(name)
5
+ "#{name.gsub(/[^a-zA-Z0-9_]/, "_")}_#{SecureRandom.hex(4)}".gsub(/_+/, '_')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,94 @@
1
+ module Terraformer
2
+ module Resource
3
+ class CloudWatchAlarm
4
+ include Terraformer::Templating
5
+
6
+ def self.execute(options, client: Aws::CloudWatch::Client)
7
+ self.new(options, client).execute
8
+ end
9
+
10
+ def initialize(options, client)
11
+ @credentials = Terraformer::Credentials::Aws.get_from_options(options)
12
+ @client = client.new(@credentials)
13
+ end
14
+
15
+ def execute
16
+ { tf: tf, tfstate: tfstate }
17
+ end
18
+
19
+ private
20
+
21
+ def tf
22
+ apply_template("tf/cloud_watch_alarm")
23
+ end
24
+
25
+ def tfstate
26
+ alarms.inject({}) do |resources, alarm|
27
+ resources["aws_cloudwatch_metric_alarm.#{alarm.normalized_name}"] = {
28
+ "type" => "aws_cloudwatch_metric_alarm",
29
+ "depends_on" => [],
30
+ "primary" => {
31
+ "id" => alarm.alarm_name,
32
+ "attributes" => alarm_attributes(alarm)
33
+ },
34
+ "deposed" => [],
35
+ "provider" => ""
36
+ }
37
+ resources
38
+ end
39
+ end
40
+
41
+ def alarm_attributes(alarm)
42
+ attributes = {
43
+ "actions_enabled" => alarm.actions_enabled.to_s,
44
+ "alarm_description" => sanitize(alarm.alarm_description),
45
+ "alarm_name" => alarm.alarm_name,
46
+ "comparison_operator" => alarm.comparison_operator,
47
+ "evaluation_periods" => alarm.evaluation_periods.to_s,
48
+ "id" => alarm.alarm_name,
49
+ "metric_name" => alarm.metric_name,
50
+ "namespace" => alarm.namespace,
51
+ "period" => alarm.period.to_s,
52
+ "statistic" => alarm.statistic,
53
+ "threshold" => alarm.threshold.to_s,
54
+ "unit" => sanitize(alarm.unit)
55
+ }
56
+ add_checksummed_attributes(attributes, alarm)
57
+ end
58
+
59
+ def alarms
60
+ @alarms ||= begin
61
+ response = @client.describe_alarms.map(&:metric_alarms).flatten
62
+ response.each do |alarm|
63
+ def alarm.normalized_name
64
+ "#{self.alarm_name.gsub(/[^a-zA-Z0-9_]/, "_")}_#{SecureRandom.hex(4)}".gsub(/_+/, '_')
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def sanitize(argument)
71
+ argument.nil? ? "" : argument
72
+ end
73
+
74
+ def add_checksummed_attributes(attributes, alarm)
75
+ %w(insufficient_data_actions alarm_actions ok_actions dimensions).each do |action|
76
+ attribute = alarm.send(action.to_sym)
77
+ attributes["#{action}.#"] = attribute.size.to_s
78
+ attribute.each do |attr|
79
+ if attr.is_a? String
80
+ checksum = Zlib.crc32(attr)
81
+ value = attr
82
+ else
83
+ checksum = attr.name
84
+ value = attr.value
85
+ end
86
+ attributes["#{action}.#{checksum}"] = value
87
+ end
88
+ end
89
+
90
+ attributes
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,116 @@
1
+ module Terraformer
2
+ module Resource
3
+ class DatadogMonitor
4
+ include Terraformer::Templating
5
+
6
+ def self.execute(options, client: Dogapi::Client)
7
+ self.new(options, client).execute
8
+ end
9
+
10
+ def initialize(options, client)
11
+ @credentials = Terraformer::Credentials::Datadog.get_from_options(options)
12
+ @client = client.new(@credentials[:api_key], @credentials[:app_key])
13
+ end
14
+
15
+ def execute
16
+ { tf: tf, tfstate: tfstate }
17
+ end
18
+
19
+ private
20
+
21
+ def tf
22
+ apply_template("tf/datadog_monitor")
23
+ end
24
+
25
+ def tfstate
26
+ monitors.inject({}) do |resources, monitor|
27
+ resources["datadog_monitor.#{monitor["normalized_name"]}"] = {
28
+ "type" => "datadog_monitor",
29
+ "depends_on" => [],
30
+ "primary" => {
31
+ "id" => monitor["id"].to_s,
32
+ "attributes" => monitor_attributes(monitor),
33
+ "meta" => {},
34
+ "tainted" => false
35
+ },
36
+ "deposed" => [],
37
+ "provider" => ""
38
+ }
39
+ resources
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def monitors
46
+ @monitors ||= begin
47
+ response = check_response(@client.get_all_monitors)
48
+
49
+ response.each do |monitor|
50
+ monitor["normalized_name"] = Terraformer::Normalizer.module_name(monitor["name"])
51
+
52
+ # Fix missing thresholds
53
+ if monitor["options"]["thresholds"].nil?
54
+ critical = monitor["query"].split.last
55
+ monitor["options"]["thresholds"] = { "critical" => critical }
56
+ else
57
+ monitor["options"]["thresholds"].each { |k, v| monitor["options"]["thresholds"][k] = v.round }
58
+ end
59
+ end
60
+
61
+ response
62
+ end
63
+ end
64
+
65
+ def check_response(response)
66
+ unless response[0] == "200"
67
+ raise "[ERROR] Received bad response form Datadog. code: #{response[0]} response: #{response[1]}"
68
+ end
69
+
70
+ response[1]
71
+ end
72
+
73
+ def monitor_attributes(monitor)
74
+ attributes = {
75
+ "id" => monitor["id"],
76
+ "name" => monitor["name"],
77
+ "type" => monitor["type"],
78
+ "message" => monitor["message"],
79
+ "escalation_message" => monitor["escalation_message"],
80
+ "query" => monitor["query"],
81
+ "notify_no_data" => monitor["options"]["notify_no_data"],
82
+ "no_data_timeframe" => monitor["options"]["no_data_timeframe"],
83
+ "renotify_interval" => monitor["options"]["renotify_interval"],
84
+ "notify_audit" => monitor["options"]["notify_audit"],
85
+ "timeout_h" => monitor["options"]["timeout_h"],
86
+ "include_tags" => monitor["options"]["include_tags"],
87
+ "require_full_window" => monitor["options"]["name"],
88
+ "locked" => monitor["options"]["locked"]
89
+ }
90
+ %w(silenced thresholds).each do |argument|
91
+ option = monitor["options"][argument]
92
+ next if option.nil?
93
+ attributes["#{argument}.%"] = option.length
94
+ option.each { |k, v| attributes["#{argument}.#{k}"] = v }
95
+ end
96
+ attributes["tags.%"] = monitor["tags"].length
97
+ monitor["tags"].each { |tag| attributes["tags.#{tag.split(":")[0]}"] = tag.split(":")[0] }
98
+
99
+ sanitize_monitor(attributes)
100
+ end
101
+
102
+ def sanitize_monitor(attributes)
103
+ attributes = Hash[ attributes.sort_by { |k, v| k }]
104
+
105
+ attributes.each do |key, value|
106
+ attributes[key] = value.to_s
107
+ end
108
+ %w(include_tags locked require_full_window).each do |attribute|
109
+ attributes[attribute] = "false" if attributes[attribute] == ""
110
+ end
111
+
112
+ attributes
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,33 @@
1
+ <% alarms.each do |alarm| -%>
2
+ resource "aws_cloudwatch_metric_alarm" "<%= alarm.normalized_name %>" {
3
+ alarm_name = "<%= alarm.alarm_name %>"
4
+ comparison_operator = "<%= alarm.comparison_operator %>"
5
+ evaluation_periods = "<%= alarm.evaluation_periods %>"
6
+ metric_name = "<%= alarm.metric_name %>"
7
+ namespace = "<%= alarm.namespace %>"
8
+ period = "<%= alarm.period %>"
9
+ statistic = "<%= alarm.statistic %>"
10
+ threshold = "<%= alarm.threshold %>"
11
+ alarm_description = "<%= alarm.alarm_description %>"
12
+ <%- unless alarm.ok_actions.empty? -%>
13
+ ok_actions = <%= alarm.ok_actions %>
14
+ <%- end -%>
15
+ <%- unless alarm.alarm_actions.empty? -%>
16
+ alarm_actions = <%= alarm.alarm_actions %>
17
+ <%- end -%>
18
+ <%- unless alarm.actions_enabled -%>
19
+ actions_enabled = <%= alarm.actions_enabled %>
20
+ <%- end -%>
21
+ <%- unless alarm.dimensions.empty? -%>
22
+ dimensions {
23
+ <% alarm.dimensions.each do |dimension| -%>
24
+ <%= dimension.name %> = "<%= dimension.value %>"
25
+ <% end -%>
26
+ }
27
+ <%- end -%>
28
+ <%- unless alarm.insufficient_data_actions.empty? -%>
29
+ insufficient_data_actions = <%= alarm.insufficient_data_actions %>
30
+ <%- end -%>
31
+ }
32
+
33
+ <%- end -%>
@@ -0,0 +1,69 @@
1
+ <% monitors.each do |monitor| -%>
2
+ resource "datadog_monitor" "<%= monitor["normalized_name"] %>" {
3
+ name = "<%= monitor["name"] %>"
4
+ type = "<%= monitor["type"] %>"
5
+ query = "<%= monitor["query"].gsub(/"/, '\"') %>"
6
+ <%- if monitor["message"].include? "\n" -%>
7
+ message = <<EOF
8
+ <%= monitor["message"] %>
9
+ EOF
10
+ <%- else -%>
11
+ message = "<%= monitor["message"] %>"
12
+ <%- end -%>
13
+ <%- unless monitor["escalation_message"].nil? -%>
14
+ escalation_message = <%= monitor["escalation_message"] %>
15
+ <%- end -%>
16
+
17
+ thresholds {
18
+ critical = <%= monitor["options"]["thresholds"]["critical"] %>
19
+ <%- unless monitor["options"]["thresholds"]["warning"].nil? -%>
20
+ warning = <%= monitor["options"]["thresholds"]["warning"] %>
21
+ <%- end -%>
22
+ <%- unless monitor["options"]["thresholds"]["ok"].nil? -%>
23
+ ok = <%= monitor["options"]["thresholds"]["ok"] %>
24
+ <%- end -%>
25
+ }
26
+
27
+ <%- unless monitor["options"]["notify_no_data"].nil? -%>
28
+ notify_no_data = <%= monitor["options"]["notify_no_data"] %>
29
+ <%- end -%>
30
+ <%- unless monitor["options"]["no_data_timeframe "].nil? -%>
31
+ no_data_timeframe = <%= monitor["options"]["no_data_timeframe "] %>
32
+ <%- end -%>
33
+ <%- unless monitor["options"]["renotify_interval"].nil? -%>
34
+ renotify_interval = <%= monitor["options"]["renotify_interval"] %>
35
+ <%- end -%>
36
+ <%- unless monitor["options"]["notify_audit"].nil? -%>
37
+ notify_audit = <%= monitor["options"]["notify_audit"] %>
38
+ <%- end -%>
39
+ <%- unless monitor["options"]["timeout_h"].nil? -%>
40
+ timeout_h = <%= monitor["options"]["timeout_h"] %>
41
+ <%- end -%>
42
+ <%- unless monitor["options"]["include_tags"].nil? -%>
43
+ include_tags = <%= monitor["options"]["include_tags"] %>
44
+ <%- end -%>
45
+ <%- unless monitor["options"]["require_full_window"].nil? -%>
46
+ require_full_window = <%= monitor["options"]["require_full_window"] %>
47
+ <%- end -%>
48
+ <%- unless monitor["options"]["locked"].nil? -%>
49
+ locked = <%= monitor["options"]["locked"] %>
50
+ <%- end -%>
51
+ <%- unless monitor["options"]["silenced"].nil? || monitor["options"]["silenced"].empty? -%>
52
+
53
+ silenced {
54
+ <% monitor["options"]["silenced"].each do |key, value| -%>
55
+ "<%= key %>" = "<%= value || 0 %>"
56
+ <%- end -%>
57
+ }
58
+ <%- end -%>
59
+ <%- unless monitor["options"]["tags"].nil? || monitor["options"]["tags"].empty? -%>
60
+
61
+ tags {
62
+ <% monitor["options"]["tags"].each do |tag| -%>
63
+ "<%= tag.split(":")[0] %>" = "<%= tag.split(":")[1] %>"
64
+ <%- end -%>
65
+ }
66
+ <%- end -%>
67
+ }
68
+
69
+ <%- end -%>
@@ -0,0 +1,11 @@
1
+ module Terraformer
2
+ module Templating
3
+ def apply_template(erb)
4
+ ERB.new(open(template_path(erb)).read, nil, "-").result(binding)
5
+ end
6
+
7
+ def template_path(template_name)
8
+ File.join(File.expand_path(File.dirname(__FILE__)), "template", template_name) << ".erb"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,82 @@
1
+ module Terraformer
2
+ class TerraformFileNotFound < RuntimeError
3
+ end
4
+
5
+ class TerraformInvalidTfstateFile < RuntimeError
6
+ end
7
+
8
+ class Terraform
9
+ class << self
10
+ def execute(data, klass_name, options)
11
+ tf(data[:tf], klass_name, options[:name])
12
+ tfstate(data[:tfstate], options[:merge])
13
+ end
14
+
15
+ def tf(generated_tf, klass_name, tf_path)
16
+ tf_file_name = "#{klass_name.split('::')[-1].downcase}.tf"
17
+ tf_file_name = tf_path unless tf_path.nil?
18
+
19
+ if File.file?(tf_file_name)
20
+ UserInput.ask("File: #{tf_file_name} already exists. Overwrite?", "Y", "n")
21
+ end
22
+
23
+ File.open(tf_file_name, "w") do |f|
24
+ f.write(generated_tf)
25
+ end
26
+ end
27
+
28
+ def tfstate(generated_tfstate, tfstate_path)
29
+ unless tfstate_path.nil?
30
+ if File.file?(tfstate_path)
31
+ tfstate = parse_json_file(tfstate_path)
32
+ else
33
+ puts "[ERROR] File not found while trying to merge state file with: #{tfstate_path}."
34
+ raise TerraformFileNotFound
35
+ end
36
+ else
37
+ tfstate_path = "terraform.tfstate"
38
+ if File.file?(tfstate_path)
39
+ UserInput.ask("File: #{tfstate_path} already exists. Overwrite?", "Y", "n")
40
+ end
41
+ tfstate = tfstate_skeleton
42
+ end
43
+
44
+ tfstate["serial"] = tfstate["serial"] + 1
45
+ tfstate["modules"][0]["resources"] = tfstate["modules"][0]["resources"].merge(generated_tfstate)
46
+ state_file = JSON.pretty_generate(tfstate)
47
+
48
+ File.open(tfstate_path, "w+") do |f|
49
+ f.write(state_file)
50
+ f.flush
51
+ end
52
+ end
53
+
54
+ def parse_json_file(file_path)
55
+ file_content = File.read(file_path)
56
+ JSON.parse(file_content)
57
+ rescue JSON::ParserError
58
+ puts "[ERROR] Unable to parse: #{file_path}."
59
+ raise TerraformInvalidTfstateFile
60
+ end
61
+
62
+ def tfstate_skeleton
63
+ {
64
+ "version" => 3,
65
+ "terraform_version" => "0.7.3",
66
+ "serial" => 0,
67
+ "lineage" => SecureRandom.uuid,
68
+ "modules" => [
69
+ {
70
+ "path" => [
71
+ "root"
72
+ ],
73
+ "outputs" => {},
74
+ "resources" => {},
75
+ "depends_on" => []
76
+ }
77
+ ]
78
+ }
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,33 @@
1
+ module Terraformer
2
+ class UserInputError < RuntimeError
3
+ end
4
+
5
+ class UserInputMaxAttemptsError < RuntimeError
6
+ end
7
+
8
+ class UserInput
9
+ MAX_ATTEMPTS = 10
10
+
11
+ class << self
12
+ def get_input
13
+ response = gets
14
+ response.chomp
15
+ end
16
+
17
+ def ask(message, success, failure)
18
+ (0..MAX_ATTEMPTS).each do |i|
19
+ puts "#{message} [#{success}/#{failure}]"
20
+ response = get_input
21
+
22
+ if response.downcase == success.downcase || response == ""
23
+ return true
24
+ elsif response.downcase == failure.downcase
25
+ puts "Exiting"
26
+ raise UserInputError
27
+ end
28
+ end
29
+ raise UserInputMaxAttemptsError
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Terraformer
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'terraformer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "terraform-exporter"
8
+ spec.version = Terraformer::VERSION
9
+ spec.authors = ["Jacopo Scrinzi"]
10
+ spec.email = ["jacopo@intercom.io"]
11
+
12
+ spec.summary = %q{Export existing resources to Terraform style (tf, tfstate)}
13
+ spec.description = %q{Export existing resources to Terraform style (tf, tfstate)}
14
+ spec.homepage = "https://github.com/intercom/terraformer"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "aws-sdk", "~> 2.5.1"
23
+ spec.add_dependency "dogapi", "~> 1.23"
24
+ spec.add_dependency "oj", "~> 2.17.1"
25
+ spec.add_dependency "ox", "~> 2.4.0"
26
+ spec.add_dependency "thor"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.7"
29
+ spec.add_development_dependency "coveralls", "~> 0.8.13"
30
+ spec.add_development_dependency "rake"
31
+ spec.add_development_dependency "rspec", "~> 3.2"
32
+ spec.add_development_dependency "simplecov", "~> 0.11.1"
33
+ end
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: terraform-exporter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Jacopo Scrinzi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.5.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.5.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: dogapi
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.23'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.23'
41
+ - !ruby/object:Gem::Dependency
42
+ name: oj
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.17.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.17.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: ox
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.4.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.4.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: coveralls
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.8.13
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.8.13
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.2'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.2'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.11.1
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 0.11.1
153
+ description: Export existing resources to Terraform style (tf, tfstate)
154
+ email:
155
+ - jacopo@intercom.io
156
+ executables:
157
+ - terraformer
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - ".gitignore"
162
+ - Gemfile
163
+ - Gemfile.lock
164
+ - Guardfile
165
+ - README.md
166
+ - Rakefile
167
+ - bin/terraformer
168
+ - lib/terraformer.rb
169
+ - lib/terraformer/cli.rb
170
+ - lib/terraformer/credentials/aws.rb
171
+ - lib/terraformer/credentials/datadog.rb
172
+ - lib/terraformer/normalizer.rb
173
+ - lib/terraformer/resource/cloud_watch_alarm.rb
174
+ - lib/terraformer/resource/datadog_monitor.rb
175
+ - lib/terraformer/template/tf/cloud_watch_alarm.erb
176
+ - lib/terraformer/template/tf/datadog_monitor.erb
177
+ - lib/terraformer/templating.rb
178
+ - lib/terraformer/terraform.rb
179
+ - lib/terraformer/user_input.rb
180
+ - lib/terraformer/version.rb
181
+ - terraformer.gemspec
182
+ homepage: https://github.com/intercom/terraformer
183
+ licenses:
184
+ - MIT
185
+ metadata: {}
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 2.5.1
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: Export existing resources to Terraform style (tf, tfstate)
206
+ test_files: []