Class: Kontena::Machine::Aws::NodeProvisioner

Inherits:
Object
  • Object
show all
Includes:
Cli::ShellSpinner, Common, RandomName
Defined in:
lib/kontena/machine/aws/node_provisioner.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Common

#default_subnet, #default_vpc, #resolve_ami, #resolve_security_groups_to_ids

Constructor Details

#initialize(api_client, access_key_id, secret_key, region) ⇒ NodeProvisioner

Returns a new instance of NodeProvisioner.

Parameters:

  • api_client (Kontena::Client)

    Kontena api client

  • access_key_id (String)

    aws_access_key_id

  • secret_key (String)

    aws_secret_access_key

  • region (String)


18
19
20
21
22
23
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 18

def initialize(api_client, access_key_id, secret_key, region)
  @api_client = api_client
  @ec2 = ::Aws::EC2::Resource.new(
    region: region, credentials: ::Aws::Credentials.new(access_key_id, secret_key)
  )
end

Instance Attribute Details

#api_clientObject (readonly)

Returns the value of attribute api_client.



12
13
14
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 12

def api_client
  @api_client
end

#ec2Object (readonly)

Returns the value of attribute ec2.



12
13
14
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 12

def ec2
  @ec2
end

Instance Method Details

#aws_dns_supported?(vpc_id) ⇒ Boolean

Parameters:

  • vpc_id (String)

Returns:

  • (Boolean)


207
208
209
210
211
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 207

def aws_dns_supported?(vpc_id)
  vpc = ec2.vpc(vpc_id)
  response = vpc.describe_attribute({attribute: 'enableDnsSupport'})
  response.enable_dns_support
end

#create_security_group(name, vpc_id) ⇒ Aws::EC2::SecurityGroup

creates security_group and authorizes default port ranges

Parameters:

  • name (String)
  • vpc_id (String)

Returns:

  • (Aws::EC2::SecurityGroup)


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 136

def create_security_group(name, vpc_id)
  sg = ec2.create_security_group({
    group_name: name,
    description: "Kontena Grid",
    vpc_id: vpc_id
  })

  sg.authorize_ingress({ # SSH
    ip_protocol: 'tcp', from_port: 22, to_port: 22, cidr_ip: '0.0.0.0/0'
  })
  sg.authorize_ingress({ # HTTP
    ip_protocol: 'tcp', from_port: 80, to_port: 80, cidr_ip: '0.0.0.0/0'
  })
  sg.authorize_ingress({ # HTTPS
    ip_protocol: 'tcp', from_port: 443, to_port: 443, cidr_ip: '0.0.0.0/0'
  })
  sg.authorize_ingress({ # OpenVPN
    ip_protocol: 'udp', from_port: 1194, to_port: 1194, cidr_ip: '0.0.0.0/0'
  })
  sg.authorize_ingress({ # Overlay / Weave network
    ip_permissions: [
      {
        from_port: 6783, to_port: 6783, ip_protocol: 'tcp',
        user_id_group_pairs: [
          { group_id: sg.group_id, vpc_id: vpc_id }
        ]
      },
      {
        from_port: 6783, to_port: 6784, ip_protocol: 'udp',
        user_id_group_pairs: [
          { group_id: sg.group_id, vpc_id: vpc_id }
        ]
      }
    ]
  })

  sg
end

#ensure_security_group(grid, vpc_id) ⇒ Array

Returns Security group id in array.

Parameters:

  • grid (String)

Returns:

  • (Array)

    Security group id in array



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 117

def ensure_security_group(grid, vpc_id)
  group_name = "kontena_grid_#{grid}"
  group_id = resolve_security_groups_to_ids(group_name, vpc_id)

  if group_id.empty?
    spinner "Creating AWS security group" do
      sg = create_security_group(group_name, vpc_id)
      group_id = [sg.group_id]
    end
  end
  group_id
end

#erb(template, vars) ⇒ Object



193
194
195
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 193

def erb(template, vars)
  ERB.new(template).result(OpenStruct.new(vars).instance_eval { binding })
end

#generate_nameObject



185
186
187
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 185

def generate_name
  "#{super}-#{rand(1..99)}"
end

#instance_exists_in_grid?(grid, name) ⇒ Boolean

Returns:

  • (Boolean)


189
190
191
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 189

def instance_exists_in_grid?(grid, name)
  api_client.get("grids/#{grid}/nodes")['nodes'].find{|n| n['name'] == name}
end

#regionString

Returns:

  • (String)


176
177
178
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 176

def region
  ec2.client.config.region
end

#run!(opts) ⇒ Object

Parameters:

  • opts (Hash)


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 26

def run!(opts)
  ami = resolve_ami(region)
  abort('No valid AMI found for region') unless ami

  opts[:vpc] = default_vpc.vpc_id unless opts[:vpc]

  security_groups = opts[:security_groups] ?
      resolve_security_groups_to_ids(opts[:security_groups], opts[:vpc]) :
      ensure_security_group(opts[:grid], opts[:vpc])

  if opts[:subnet].nil?
    subnet = default_subnet(opts[:vpc], region+opts[:zone])
  else
    subnet = ec2.subnet(opts[:subnet])
  end
  dns_server = aws_dns_supported?(opts[:vpc]) ? '169.254.169.253' : '8.8.8.8'
  instances = []
  opts[:count].to_i.times do |i|
    if opts[:name] && opts[:count].to_i > 1
      name = "#{opts[:name]}-#{i+1}"
    elsif opts[:name]
      name = opts[:name]
    else
      name = generate_name
    end

    userdata_vars = {
      name: name,
      version: opts[:version],
      master_uri: opts[:master_uri],
      grid_token: opts[:grid_token],
      dns_server: dns_server
    }

    ec2_instance = ec2.create_instances({
      image_id: ami,
      min_count: 1,
      max_count: 1,
      instance_type: opts[:type],
      key_name: opts[:key_pair],
      user_data: Base64.encode64(user_data(userdata_vars)),
      block_device_mappings: [
        {
          device_name: '/dev/xvda',
          virtual_name: 'Root',
          ebs: {
            volume_size: opts[:storage],
            volume_type: 'gp2'
          }
        }
      ],
      network_interfaces: [
       {
         device_index: 0,
         subnet_id: subnet.subnet_id,
         groups: security_groups,
         associate_public_ip_address: opts[:associate_public_ip],
         delete_on_termination: true
       }
      ]
    }).first
    ec2_instance.create_tags({
      tags: [
        {key: 'Name', value: name},
        {key: 'kontena_grid', value: opts[:grid]}
      ]
    })

    spinner "Creating AWS instance #{name.colorize(:cyan)} " do
      sleep 1 until ec2_instance.reload.state.name == 'running'
    end
    instances << name
  end
  instances.each do |instance_name|
    node = nil
    spinner "Waiting for node #{instance_name.colorize(:cyan)} join to grid #{opts[:grid].colorize(:cyan)} " do
      sleep 1 until node = instance_exists_in_grid?(opts[:grid], instance_name)
    end
    labels = [
      "region=#{region}",
      "az=#{opts[:zone]}",
      "provider=aws",
      "type=#{opts[:type]}"
    ]
    set_labels(node, labels)
  end
end

#set_labels(node, labels) ⇒ Object

Parameters:

  • node (Hash)
  • labels (Array<String>)


199
200
201
202
203
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 199

def set_labels(node, labels)
  data = {}
  data[:labels] = labels
  api_client.put("nodes/#{node['id']}", data, {}, {'Kontena-Grid-Token' => node['grid']['token']})
end

#user_data(vars) ⇒ Object



180
181
182
183
# File 'lib/kontena/machine/aws/node_provisioner.rb', line 180

def user_data(vars)
  cloudinit_template = File.join(__dir__ , '/cloudinit.yml')
  erb(File.read(cloudinit_template), vars)
end