Module: Kitchen::Driver::Mixins::DedicatedHosts
- Included in:
- Ec2
- Defined in:
- lib/kitchen/driver/aws/dedicated_hosts.rb
Overview
Allocation and release of EC2 Dedicated Hosts.
A dedicated host is physical hardware reserved for one account, required
for tenancy: host and for platforms such as macOS. Hosts are billed
from allocation until release regardless of whether an instance is
running on them, so both operations are gated behind explicit config and
failures are fatal rather than warnings.
Only hosts tagged ManagedBy: Test Kitchen are ever considered, so a
user's own dedicated hosts are never allocated to or released.
This module expects its includer to provide config, ec2 and the
Test Kitchen logging methods; it is mixed into Ec2.
Instance Method Summary collapse
-
#allocate_host ⇒ String
Allocate a new dedicated host for the configured instance type.
-
#allow_allocate_host? ⇒ Boolean
Whether the user has opted in to allocating dedicated hosts.
-
#allow_deallocate_host? ⇒ Boolean
Whether the user has opted in to releasing dedicated hosts.
-
#deallocate_host(host_id) ⇒ nil
Release a dedicated host.
-
#host_available? ⇒ Boolean
Whether any managed host has room for the configured instance type.
-
#host_for_id(host_id) ⇒ Aws::EC2::Types::Host?
Look a dedicated host up by ID.
-
#host_unused?(host) ⇒ Boolean
Whether a host has no instances running on it.
-
#hosts_managed ⇒ Array<Aws::EC2::Types::Host>
Available dedicated hosts that Test Kitchen allocated.
-
#hosts_with_capacity ⇒ Array<Aws::EC2::Types::Host>
Managed hosts with room for the configured instance type.
-
#instance_family_from_type(instance_type) ⇒ String
The family part of an instance type.
-
#instance_size_from_type(instance_type) ⇒ String
The size part of an instance type.
-
#metal_instance_type?(instance_type) ⇒ Boolean
Whether an instance type runs on bare metal.
Instance Method Details
#allocate_host ⇒ String
Terminates the process with exit! when allocation is not
enabled or no availability zone is configured, since an allocated
host costs money whether or not it is used.
Allocate a new dedicated host for the configured instance type.
A bare-metal size occupies a whole host, so it is allocated for that exact type; every other size can share a host, so the whole instance family is allocated and EC2 places instances within it.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 92 def allocate_host unless allow_allocate_host? warn "ERROR: Attempted to allocate a dedicated host, but the driver setting `allocate_dedicated_host` is not enabled. " \ "Set `allocate_dedicated_host: true` to allow it, remembering that a dedicated host is billed from allocation " \ "until it is released." exit! end unless config[:availability_zone] warn "Attempted to allocate dedicated host but option 'availability_zone' is not set" exit! end info("Allocating dedicated host for #{config[:instance_type]} instances. This will incur additional cost") request = { availability_zone: config[:availability_zone], quantity: 1, auto_placement: "on", tag_specifications: [ { resource_type: "dedicated-host", tags: [ { key: "ManagedBy", value: "Test Kitchen" }, ], }, ], } # Bare metal is a 1:1 association, everything else has multi-instance capability if (config[:instance_type]) request[:instance_type] = config[:instance_type] else request[:instance_family] = instance_family_from_type(config[:instance_type]) end response = ec2.client.allocate_hosts(request) response.host_ids.first end |
#allow_allocate_host? ⇒ Boolean
Whether the user has opted in to allocating dedicated hosts.
183 184 185 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 183 def allow_allocate_host? config[:allocate_dedicated_host] end |
#allow_deallocate_host? ⇒ Boolean
Whether the user has opted in to releasing dedicated hosts.
190 191 192 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 190 def allow_deallocate_host? config[:deallocate_dedicated_host] end |
#deallocate_host(host_id) ⇒ nil
Terminates the process with exit! when the release fails, as a
host that stays allocated keeps accruing charges silently.
Release a dedicated host.
140 141 142 143 144 145 146 147 148 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 140 def deallocate_host(host_id) info("Deallocating dedicated host #{host_id}") response = ec2.client.release_hosts({ host_ids: [host_id] }) return if response.unsuccessful.empty? warn "ERROR: Could not release dedicated host #{host_id}. Host may remain allocated and incur cost" exit! end |
#host_available? ⇒ Boolean
Whether any managed host has room for the configured instance type.
22 23 24 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 22 def host_available? !hosts_with_capacity.empty? end |
#host_for_id(host_id) ⇒ Aws::EC2::Types::Host?
Look a dedicated host up by ID.
61 62 63 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 61 def host_for_id(host_id) ec2.client.describe_hosts(host_ids: [host_id]).hosts.first end |
#host_unused?(host) ⇒ Boolean
Whether a host has no instances running on it.
52 53 54 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 52 def host_unused?(host) host.instances.empty? end |
#hosts_managed ⇒ Array<Aws::EC2::Types::Host>
Available dedicated hosts that Test Kitchen allocated.
Filtered on the ManagedBy tag so that hosts belonging to the user are
never touched, and on state so that hosts still being provisioned or
already released are ignored.
72 73 74 75 76 77 78 79 80 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 72 def hosts_managed response = ec2.client.describe_hosts( filter: [ { name: "tag:ManagedBy", values: ["Test Kitchen"] }, ] ) response.hosts.select { |host| host.state == "available" } end |
#hosts_with_capacity ⇒ Array<Aws::EC2::Types::Host>
Managed hosts with room for the configured instance type.
T-family hosts report no capacity information and may be overprovisioned, so a host with no capacity block counts as available.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 32 def hosts_with_capacity hosts_managed.select do |host| # T-instance hosts do not report available capacity and can be overprovisioned if host.available_capacity.nil? true else instance_capacity = host.available_capacity.available_instance_capacity capacity_for_type = instance_capacity.detect { |cap| cap.instance_type == config[:instance_type] } # A host that lists no capacity for this instance type cannot run # one, so treat a missing entry the same as zero rather than # raising on it. !capacity_for_type.nil? && capacity_for_type.available_capacity > 0 end end end |
#instance_family_from_type(instance_type) ⇒ String
The family part of an instance type.
154 155 156 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 154 def instance_family_from_type(instance_type) instance_type.split(".").first end |
#instance_size_from_type(instance_type) ⇒ String
The size part of an instance type.
162 163 164 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 162 def instance_size_from_type(instance_type) instance_type.split(".").last end |
#metal_instance_type?(instance_type) ⇒ Boolean
Whether an instance type runs on bare metal.
Older families expose a single bare-metal size named plainly ".metal". Newer ones expose several on the same family and name them ".metal-24xl", ".metal-48xl" and so on, so an equality check against "metal" would take them for virtualized sizes and allocate their host by family.
176 177 178 |
# File 'lib/kitchen/driver/aws/dedicated_hosts.rb', line 176 def (instance_type) instance_size_from_type(instance_type).start_with?("metal") end |