terrafying-components 1.4.3 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,96 @@
1
+
2
+ require 'terrafying/components/loadbalancer'
3
+ require 'terrafying/components/usable'
4
+ require 'terrafying/components/vpc'
5
+ require 'terrafying/generator'
6
+
7
+ module Terrafying
8
+
9
+ module Components
10
+
11
+ class Endpoint < Terrafying::Context
12
+
13
+ attr_reader :security_group
14
+
15
+ include Usable
16
+
17
+ def self.create_in(vpc, name, options={})
18
+ Endpoint.new.create_in(vpc, name, options)
19
+ end
20
+
21
+ def initialize
22
+ super
23
+ end
24
+
25
+ def create_in(vpc, name, options={})
26
+ options = {
27
+ auto_accept: true,
28
+ subnets: vpc.subnets.fetch(:private, []),
29
+ private_dns: false,
30
+ dns_name: name,
31
+ tags: {},
32
+ }.merge(options)
33
+
34
+ ident = "#{tf_safe(vpc.name)}-#{name}"
35
+ @name = name
36
+
37
+ if options[:service]
38
+ service_name = options[:service].service_name
39
+
40
+ @ports = options[:service].load_balancer.ports
41
+ elsif options[:service_name]
42
+ service_name = options[:service_name]
43
+
44
+ if options[:service_name].start_with?("com.amazonaws")
45
+ @ports = enrich_ports([443])
46
+ else
47
+ endpoint_service = aws.endpoint_service_by_name(options[:service_name])
48
+
49
+ target_groups = endpoint_service.network_load_balancer_arns.map { |arn|
50
+ aws.target_groups_by_lb(arn)
51
+ }.flatten
52
+
53
+ @ports = enrich_ports(target_groups.map(&:port))
54
+ end
55
+ elsif options[:source]
56
+ if options[:source].is_a?(VPC)
57
+ source = { vpc: options[:source], name: name }
58
+ else
59
+ source = options[:source]
60
+ end
61
+
62
+ lb = LoadBalancer.find_in(source[:vpc], source[:name])
63
+
64
+ @ports = lb.ports
65
+ service_name = aws.endpoint_service_by_lb_arn(lb.id).service_name
66
+ else
67
+ raise "You need to pass either a service_name or source option to create an endpoint"
68
+ end
69
+
70
+ @security_group = resource :aws_security_group, ident, {
71
+ name: "endpoint-#{ident}",
72
+ description: "Describe the ingress and egress of the endpoint #{ident}",
73
+ tags: options[:tags],
74
+ vpc_id: vpc.id,
75
+ }
76
+
77
+ resource :aws_vpc_endpoint, ident, {
78
+ vpc_id: vpc.id,
79
+ service_name: service_name,
80
+ vpc_endpoint_type: "Interface",
81
+ security_group_ids: [ @security_group ],
82
+ auto_accept: options[:auto_accept],
83
+ subnet_ids: options[:subnets].map(&:id),
84
+ private_dns_enabled: options[:private_dns],
85
+ }
86
+
87
+ vpc.zone.add_cname_in(self, options[:dns_name], output_of(:aws_vpc_endpoint, ident, "dns_entry.0.dns_name"))
88
+
89
+ self
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,63 @@
1
+
2
+ require 'terrafying/generator'
3
+
4
+ module Terrafying
5
+
6
+ module Components
7
+
8
+ class EndpointService < Terrafying::Context
9
+
10
+ attr_reader :name, :load_balancer, :service_name
11
+
12
+ def self.create_for(load_balancer, name, options={})
13
+ EndpointService.new.create_for(load_balancer, name, options)
14
+ end
15
+
16
+ def self.find(service_name)
17
+ EndpointService.new.find(service_name)
18
+ end
19
+
20
+ def initialize
21
+ super
22
+ end
23
+
24
+ def find(service_name)
25
+ raise 'unimplemented'
26
+ end
27
+
28
+ def create_for(load_balancer, name, options={})
29
+ options = {
30
+ acceptance_required: true,
31
+ allowed_principals: [
32
+ "arn:aws:iam::#{aws.account_id}:root",
33
+ ],
34
+ }.merge(options)
35
+
36
+ if ! load_balancer or load_balancer.type != "network"
37
+ raise "The load balancer needs to be a network load balancer"
38
+ end
39
+
40
+ @name = name
41
+ @load_balancer = load_balancer
42
+
43
+ resource :aws_vpc_endpoint_service, name, {
44
+ acceptance_required: options[:acceptance_required],
45
+ allowed_principals: options[:allowed_principals],
46
+ network_load_balancer_arns: [load_balancer.id],
47
+ }
48
+
49
+ @service_name = output_of(:aws_vpc_endpoint_service, name, "service_name")
50
+
51
+ self
52
+ end
53
+
54
+ def expose_in(vpc, options={})
55
+ name = options.fetch(:name, @name)
56
+ add! Endpoint.create_in(vpc, name, options.merge({ service: self }))
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,117 @@
1
+ require 'erb'
2
+ require 'ostruct'
3
+
4
+ module Terrafying
5
+
6
+ module Components
7
+
8
+ class Ignition
9
+
10
+ UNIT_REQUIRED_KEYS = [:name]
11
+ FILE_REQUIRED_KEYS = [:path, :mode, :contents]
12
+
13
+ def self.container_unit(name, image, options={})
14
+ options = {
15
+ volumes: [],
16
+ environment_variables: [],
17
+ arguments: [],
18
+ require_units: [],
19
+ host_networking: false,
20
+ privileged: false,
21
+ }.merge(options)
22
+
23
+ if options[:require_units].count > 0
24
+ require_units = options[:require_units].join(" ")
25
+ require = <<EOF
26
+ After=#{require_units}
27
+ Requires=#{require_units}
28
+ EOF
29
+ end
30
+
31
+ docker_options = []
32
+
33
+ if options[:environment_variables].count > 0
34
+ docker_options += options[:environment_variables].map { |var|
35
+ "-e #{var}"
36
+ }
37
+ end
38
+
39
+ if options[:volumes].count > 0
40
+ docker_options += options[:volumes].map { |volume|
41
+ "-v #{volume}"
42
+ }
43
+ end
44
+
45
+ if options[:host_networking]
46
+ docker_options << "--net=host"
47
+ end
48
+
49
+ if options[:privileged]
50
+ docker_options << "--privileged"
51
+ end
52
+
53
+ docker_options_str = " \\\n" + docker_options.join(" \\\n")
54
+
55
+ if options[:arguments].count > 0
56
+ arguments = " \\\n" + options[:arguments].join(" \\\n")
57
+ end
58
+
59
+ {
60
+ name: "#{name}.service",
61
+ contents: <<EOF
62
+ [Install]
63
+ WantedBy=multi-user.target
64
+
65
+ [Unit]
66
+ Description=#{name}
67
+ #{require}
68
+
69
+ [Service]
70
+ ExecStartPre=-/usr/bin/docker rm -f #{name}
71
+ ExecStart=/usr/bin/docker run --name #{name} #{docker_options_str} \
72
+ #{image} #{arguments}
73
+ Restart=always
74
+ RestartSec=30
75
+
76
+ EOF
77
+ }
78
+ end
79
+
80
+ def self.generate(options={})
81
+ options = {
82
+ keypairs: [],
83
+ volumes: [],
84
+ files: [],
85
+ units: [],
86
+ ssh_group: "cloud",
87
+ disable_update_engine: false,
88
+ region: Terrafying::Generator.aws.region,
89
+ }.merge(options)
90
+
91
+ if ! options[:units].all? { |u| UNIT_REQUIRED_KEYS.all? { |key| u.has_key?(key) } }
92
+ raise "All units require the following keys: #{UNIT_REQUIRED_KEYS}"
93
+ end
94
+
95
+ if ! options[:units].all? { |u| u.has_key?(:contents) || u.has_key?(:dropins) }
96
+ raise "All units have to have contents and/or dropins"
97
+ end
98
+
99
+ if ! options[:files].all? { |f| FILE_REQUIRED_KEYS.all? { |key| f.has_key?(key) } }
100
+ raise "All files require the following keys: #{FILE_REQUIRED_KEYS}"
101
+ end
102
+
103
+ options[:cas] = options[:keypairs].map { |kp| kp[:ca] }.sort.uniq
104
+
105
+ erb_path = File.join(File.dirname(__FILE__), "templates/ignition.yaml")
106
+ erb = ERB.new(IO.read(erb_path))
107
+
108
+ yaml = erb.result(OpenStruct.new(options).instance_eval { binding })
109
+
110
+ Terrafying::Util.to_ignition(yaml)
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,112 @@
1
+
2
+ require 'terrafying/components/usable'
3
+
4
+ module Terrafying
5
+
6
+ module Components
7
+
8
+ class Instance < Terrafying::Context
9
+
10
+ attr_reader :id, :name, :ip_address, :subnet
11
+
12
+ include Usable
13
+
14
+ def self.create_in(vpc, name, options={})
15
+ Instance.new.create_in vpc, name, options
16
+ end
17
+
18
+ def self.find_in(vpc, name)
19
+ Instance.new.find_in vpc, name
20
+ end
21
+
22
+ def initialize()
23
+ super
24
+ end
25
+
26
+ def find_in(vpc, name)
27
+ raise 'unimplemented'
28
+ end
29
+
30
+ def create_in(vpc, name, options={})
31
+ options = {
32
+ public: false,
33
+ instance_type: "t2.micro",
34
+ instance_profile: nil,
35
+ ports: [],
36
+ tags: {},
37
+ security_groups: [],
38
+ depends_on: [],
39
+ }.merge(options)
40
+
41
+ ident = "#{tf_safe(vpc.name)}-#{name}"
42
+
43
+ @name = name
44
+ @ports = enrich_ports(options[:ports])
45
+
46
+ @security_group = resource :aws_security_group, ident, {
47
+ name: "instance-#{ident}",
48
+ description: "Describe the ingress and egress of the instance #{ident}",
49
+ tags: options[:tags],
50
+ vpc_id: vpc.id,
51
+ egress: [
52
+ {
53
+ from_port: 0,
54
+ to_port: 0,
55
+ protocol: -1,
56
+ cidr_blocks: ["0.0.0.0/0"],
57
+ }
58
+ ],
59
+ }
60
+
61
+ path_mtu_setup!
62
+
63
+ if options.has_key? :ip_address
64
+ lifecycle = {
65
+ lifecycle: { create_before_destroy: false },
66
+ }
67
+ else
68
+ lifecycle = {
69
+ lifecycle: { create_before_destroy: true },
70
+ }
71
+ end
72
+
73
+ if options.has_key? :subnet
74
+ @subnet = options[:subnet]
75
+ else
76
+ subnets = options.fetch(:subnets, vpc.subnets[:private])
77
+ # pick something consistent but not just the first subnet
78
+ subnet_index = XXhash.xxh32(ident) % subnets.count
79
+ @subnet = subnets[subnet_index]
80
+ end
81
+
82
+ @id = resource :aws_instance, ident, {
83
+ ami: options[:ami],
84
+ instance_type: options[:instance_type],
85
+ iam_instance_profile: options[:instance_profile] && options[:instance_profile].id,
86
+ subnet_id: @subnet.id,
87
+ associate_public_ip_address: options[:public],
88
+ root_block_device: {
89
+ volume_type: 'gp2',
90
+ volume_size: 32,
91
+ },
92
+ tags: {
93
+ 'Name' => ident,
94
+ }.merge(options[:tags]),
95
+ vpc_security_group_ids: [
96
+ vpc.internal_ssh_security_group,
97
+ ].push(*options[:security_groups]),
98
+ user_data: options[:user_data],
99
+ lifecycle: {
100
+ create_before_destroy: true,
101
+ },
102
+ depends_on: options[:depends_on],
103
+ }.merge(options[:ip_address] ? { private_ip: options[:ip_address] } : {}).merge(lifecycle)
104
+
105
+ @ip_address = output_of(:aws_instance, ident, options[:public] ? :public_ip : :private_ip)
106
+
107
+ self
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,81 @@
1
+
2
+ module Terrafying
3
+
4
+ module Components
5
+
6
+ class InstanceProfile < Terrafying::Context
7
+
8
+ attr_reader :id
9
+
10
+ def self.create(name, options={})
11
+ InstanceProfile.new.create name, options
12
+ end
13
+
14
+ def self.find(name)
15
+ InstanceProfile.new.find name
16
+ end
17
+
18
+ def initialize()
19
+ super
20
+ end
21
+
22
+ def find(name)
23
+ raise 'unimplemented'
24
+ end
25
+
26
+ def create(name, options={})
27
+ options = {
28
+ statements: [],
29
+ }.merge(options)
30
+
31
+ resource :aws_iam_role, name, {
32
+ name: name,
33
+ assume_role_policy: JSON.pretty_generate(
34
+ {
35
+ Version: "2012-10-17",
36
+ Statement: [
37
+ {
38
+ Effect: "Allow",
39
+ Principal: { "Service": "ec2.amazonaws.com"},
40
+ Action: "sts:AssumeRole"
41
+ }
42
+ ]
43
+ }
44
+ )
45
+ }
46
+
47
+ resource :aws_iam_role_policy, name, {
48
+ name: name,
49
+ policy: JSON.pretty_generate(
50
+ {
51
+ Version: "2012-10-17",
52
+ Statement: [
53
+ {
54
+ Sid: "Stmt1442396947000",
55
+ Effect: "Allow",
56
+ Action: [
57
+ "iam:GetGroup",
58
+ "iam:GetSSHPublicKey",
59
+ "iam:GetUser",
60
+ "iam:ListSSHPublicKeys"
61
+ ],
62
+ Resource: [
63
+ "arn:aws:iam::*"
64
+ ]
65
+ }
66
+ ].push(*options[:statements])
67
+ }
68
+ ),
69
+ role: output_of(:aws_iam_role, name, :name)
70
+ }
71
+
72
+ @id = resource :aws_iam_instance_profile, name, {
73
+ name: name,
74
+ role: output_of(:aws_iam_role, name, :name),
75
+ }
76
+
77
+ self
78
+ end
79
+ end
80
+ end
81
+ end