File: instance_collection.rb

package info (click to toggle)
ruby-aws-sdk 1.67.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,840 kB
  • sloc: ruby: 28,436; makefile: 7
file content (403 lines) | stat: -rw-r--r-- 15,098 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

require 'base64'

module AWS
  class EC2

    # Represents a collection of EC2 instances.  Typically you
    # should get an instance of this class by calling
    # {EC2#instances}.
    #
    # To run an instance:
    #
    #     ec2.instances.create(:image_id => "ami-1b814f72")
    #
    # To get an instance by ID:
    #
    #     i = ec2.instances["i-12345678"]
    #     i.exists?
    #
    # To get a map of instance IDs to instance status:
    #
    #      ec2.instances.inject({}) { |m, i| m[i.id] = i.status; m }
    #     # => { "i-12345678" => :running, "i-87654321" => :shutting_down }
    #
    class InstanceCollection < Collection

      include TaggedCollection
      include BlockDeviceMappings

      # Runs one or more EC2 instances.
      #
      # @example Running a single instance
      #   i = ec2.instances.create(:image_id => "ami-8c1fece5")
      #   sleep 10 while i.status == :pending
      #
      # @example Running multiple instances with the same parameters
      #
      #   instances = ec2.instances.create(
      #     :image_id => "ami-8c1fece5",
      #     :count => 10)
      #
      #   sleep 1 while instances.any? {|i| i.status == :pending }
      #
      # @example Specifying block device mappings
      #
      #   ec2.instances.create({
      #     :image_id => "ami-8c1fece5",
      #     :block_device_mappings => [{
      #       :device_name => "/dev/sda2",
      #       :ebs => {
      #         :volume_size => 15, # 15 GiB
      #         :delete_on_termination => true
      #       }
      #     }]
      #   })
      #
      # @example Launching in an Amazon VPC subnet
      #
      #   ec2.instances.create(
      #     :image_id => "ami-8c1fece5",
      #     :subnet => "subnet-abc123ef")
      #
      # @param [Hash] options Options for new instance.  `:image_id` is
      #   the only required option.
      #
      # @option options [Integer] :count How many instances to request.  By
      #   default one instance is requested.  You can specify this
      #   either as an integer or as a Range, to indicate the
      #   minimum and maximum number of instances to run.  Note that
      #   for a new account you can request at most 20 instances at
      #   once.
      #
      # @option options [String] :iam_instance_profile The name or
      #   ARN of an IAM instance profile.  This provides credentials
      #   to the EC2 instance(s) via the instance metadata service.
      #
      #
      # @option options [Array<Hash>] :block_device_mappings Specifies how block
      #   devices are exposed to the instance. Each mapping is made up of a
      #   virtualName and a deviceName.
      #
      #     * `:virtual_name` - (String) Specifies the virtual device name.
      #     * `:device_name` - (String) Specifies the device name (e.g.,
      #       /dev/sdh).
      #     * `:ebs` - (Hash) Specifies parameters used to automatically setup
      #       Amazon EBS volumes when the instance is launched.
      #       * `:snapshot_id` - (String) The ID of the snapshot from which the
      #         volume will be created.
      #       * `:volume_size` - (Integer) The size of the volume, in
      #         gigabytes.
      #       * `:delete_on_termination` - (Boolean) Specifies whether the
      #         Amazon EBS volume is deleted on instance termination.
      #       * `:volume_type` - (String) Valid values include:
      #         * `standard`
      #         * `io1`
      #         * `gp2`
      #       * `:iops` - (Integer)
      #     * `:no_device` - (String) Specifies the device name to suppress
      #       during instance launch.
      #
      # @option options [Boolean] :monitoring_enabled Setting this to
      #   `true` enables CloudWatch monitoring on the instances once they
      #   are started.
      #
      # @option options [String] :availability_zone Specifies the
      #   availability zone where the instance should run.  Without
      #   this option, EC2 will choose an availability zone for you.
      #
      # @option options [String] :placement_group Specifies the
      #   cluster placement group where the instance should run.
      #
      # @option options [String] :image_id ID of the AMI you want to
      #   launch.
      #
      # @option options [String] :key_name The name of the key pair to
      #   use.  Note: Launching public images without a key pair ID
      #   will leave them inaccessible.
      #
      # @option options [KeyPair] :key_pair A {KeyPair} that should
      #   be used when launching an instance.
      #
      # @option options [Array] :security_groups Security groups are used
      #   to determine network access rules for the instances.
      #   `:security_groups` can be a single value or an array of values.
      #   Values should be group name strings or {SecurityGroup} objects.
      #
      # @option options [Array<String>] :security_group_ids Security groups
      #   are used to determine network access rules for the instances.
      #   `:security_group_ids` accepts a single ID or an array of security
      #   group IDs.
      #
      # @option options [String] :user_data Arbitrary user data.  You
      #   do not need to encode this value.
      #
      # @option options [String] :instance_type The type of instance to
      #   launch, for example "m1.small".
      #
      # @option options [String] :kernel_id The ID of the kernel with
      #   which to launch the instance.
      #
      # @option options [String] :ramdisk_id The ID of the RAM disk to
      #   select. Some kernels require additional drivers at
      #   launch. Check the kernel requirements for information on
      #   whether you need to specify a RAM disk. To find kernel
      #   requirements, refer to the Resource Center and search for
      #   the kernel ID.
      #
      # @option options [Boolean] :disable_api_termination Specifies
      #   whether you can terminate the instance using the EC2
      #   API. A value of true means you can't terminate the
      #   instance using the API (i.e., the instance is "locked"); a
      #   value of false means you can. If you set this to true, and
      #   you later want to terminate the instance, you must first
      #   enable API termination.  For example:
      #
      #       i = ec2.instances.create(:image_id => "ami-8c1fece5",
      #                                :disable_api_termination => true)
      #       i.api_termination_disabled?        # => true
      #       i.terminate                        # raises an exception
      #       i.api_termination_disabled = false
      #       i.terminate                        # terminates the instance
      #
      # @option options [String] :instance_initiated_shutdown_behavior
      #   Determines whether the instance stops or terminates on
      #   instance-initiated shutdown.
      #
      # @option options [Subnet,String] :subnet (nil) The VPC Subnet (or
      #   subnet id string) to launch the instance in.
      #
      # @option options [String] :private_ip_address (nil) If you're using VPC,
      #   you can optionally use this option to assign the instance a
      #   specific available IP address from the subnet (e.g., '10.0.0.25').
      #   This option is not valid for instances launched outside a VPC (i.e.
      #   those launched without the :subnet option).
      #
      # @option options [Boolean] :dedicated_tenancy (false) Instances
      #   with dedicated tenancy will not share physical hardware with
      #   instances outside their VPC.  *NOTE:* Dedicated tenancy
      #   incurs an additional service charge.  This option is not
      #   valid for instances launched outside a VPC (e.g. those
      #   launched without the :subnet option).
      #
      # @option options [Boolean] :ebs_optimized (false) EBS-Optimized instances
      #   enable Amazon EC2 instances to fully utilize the IOPS provisioned on
      #   an EBS volume. EBS-optimized instances deliver dedicated throughput
      #   between Amazon EC2 and Amazon EBS, with options between 500 Mbps and
      #   1000 Mbps depending on the instance type used. When attached to
      #   EBS-Optimized instances, Provisioned IOPS volumes are designed
      #   to deliver within 10% of their provisioned performance 99.9% of the time.
      #   *NOTE:* EBS Optimized instances incur an additional service charge. This
      #   optional is only valid for certain instance types.
      #
      # @option options [Boolean] :associate_public_ip_address (false)
      #
      # @return [Instance or Array] If a single instance is being created,
      #   this returns an {EC2::Instance} to represent the newly
      #   created instance.  Otherwise it returns an array of instance
      #   objects.
      #
      def create options = {}

        if profile = options.delete(:iam_instance_profile)
          profile = case profile
          when /^arn:aws:iam::/ then { :arn => profile }
          when String then { :name => profile }
          when Hash then profile
          else
            msg = "expected a name or ARN string for :iam_instance_profile"
          end
          options[:iam_instance_profile] = profile
        end

        if image = options.delete(:image)
          options[:image_id] = image.id
        end

        if kernel = options.delete(:kernel)
          options[:kernel_id] = kernel.id
        end

        if ramdisk = options.delete(:ramdisk)
          options[:ramdisk_id] = ramdisk.id
        end

        if key_pair = options.delete(:key_pair)
          options[:key_name] = key_pair.name
        end

        options = count_options(options).merge(options)
        options.delete(:count)

        options[:user_data] = Base64.encode64(options[:user_data]).strip if
          options[:user_data]

        if options[:block_device_mappings].is_a?(Hash)
          options[:block_device_mappings] =
            translate_block_device_mappings(options[:block_device_mappings])
        end

        options[:monitoring] = { :enabled => true } if
          options[:monitoring_enabled]
        options.delete(:monitoring_enabled)

        placement = {}

        if options[:availability_zone]
          placement[:availability_zone] = options[:availability_zone].to_s
          options.delete(:availability_zone)
        end

        if options[:placement_group]
          placement[:group_name] = options[:placement_group].to_s
          options.delete(:placement_group)
        end

        if options[:dedicated_tenancy]
          placement[:tenancy] = 'dedicated'
          options.delete(:dedicated_tenancy)
        end

        options[:placement] = placement unless placement.empty?

        network_interface = {}

        if options[:associate_public_ip_address]
          if subnet_id = subnet_id_option(options)
            network_interface[:subnet_id] = subnet_id
            options.delete(:subnet)
            options.delete(:subnet_id)
          end
          if private_ip_address = options.delete(:private_ip_address)
            network_interface[:private_ip_address] = private_ip_address
          end
          if security_group_ids = options.delete(:security_group_ids)
            network_interface[:groups] = Array(security_group_ids)
          end
          network_interface[:associate_public_ip_address] = true
          network_interface[:device_index] = 0
        end
        options.delete(:associate_public_ip_address)

        options[:network_interfaces] = [network_interface] unless network_interface.empty?

        if subnet_id = subnet_id_option(options)
          options[:subnet_id] = subnet_id
        end

        security_group_opts(options)
        
        options[:client_token] = SecureRandom.uuid unless options[:client_token]

        resp = client.run_instances(options)

        if options[:min_count] == options[:max_count] and
            options[:min_count] == 1
          self[resp.instances_set.first.instance_id]
        else
          resp[:instances_set].map {|i| self[i[:instance_id]] }
        end
      end

      alias_method :run, :create

      # @yield [Instance] Yields each instance in the collection.
      def each(&block)
        response = filtered_request(:describe_instances)
        response.reservation_set.each do |reservation|
          reservation.instances_set.each do |i|
            instance = Instance.new_from(:describe_instances, i,
              i.instance_id, :config => config)
            yield(instance)
          end
        end
      end

      # @return [Instance] Returns an object representing the EC2 instance
      #   with the given ID.
      def [] id
        super
      end

      # @api private
      protected
      def member_class
        Instance
      end

      # @api private
      private
      def count_options options
        min = max = 1
        count = options[:count]
        case count
        when Range
          min = count.begin
          max = (count.exclude_end? ? count.end - 1 : count.end)
        when Integer
          min = max = count
        end
        { :min_count => min, :max_count => max }
      end

      # @api private
      private
      def security_group_opts options

        ids = []
        names = []

        Array(options.delete(:security_group_ids)).each do |g|
          ids << g
        end

        # this may be security group objects or names
        Array(options.delete(:security_groups)).each do |g|
          case g
          when String        then names << g
          when SecurityGroup then ids << g.id
          else
            raise ArgumentError, ':security_groups may only contain ' +
              'security group names, ids or objects'
          end
        end

        return if ids.empty? and names.empty?

        if options[:subnet_id]

          # vpc instances only accepts security group ids, so any group
          # names must be converted to ids, which requires a service
          # request
          unless names.empty?
            ec2 = EC2.new(:config => config)
            groups = ec2.security_groups.filter('group-name', names)
            ids += groups.collect{|g| g.id }
          end
          options[:security_group_ids] = ids

        else

          # non-vpc instances accept both group names and ids, so
          # we accept whichever
          options[:security_groups] = names unless names.empty?
          options[:security_group_ids] = ids unless ids.empty?

        end
      end

    end
  end
end