Class: Cloudkeeper::Aws::Cloud

Inherits:
Object
  • Object
show all
Defined in:
lib/cloudkeeper/aws/cloud.rb

Overview

Class for AWS Cloud related operations

Constant Summary collapse

SUCCESSFUL_STATUS =
%w[completed].freeze
UNSUCCESSFUL_STATUS =
%w[deleted].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(s3service: nil, ec2service: nil) ⇒ Cloud

Note:

This method can be billed by AWS

Constructs Cloud object that can communicate with AWS cloud.



17
18
19
20
21
22
23
# File 'lib/cloudkeeper/aws/cloud.rb', line 17

def initialize(s3service: nil, ec2service: nil)
  ::Aws.config.update(Cloudkeeper::Aws::Settings['aws'].deep_symbolize_keys)
  @s3 = s3service || ::Aws::S3::Resource.new
  @ec2 = ec2service || ::Aws::EC2::Client.new
  @bucket = s3.bucket(Cloudkeeper::Aws::Settings['bucket-name'])
  bucket.create unless bucket.exists?
end

Instance Attribute Details

#bucketObject (readonly)

Returns the value of attribute bucket.



9
10
11
# File 'lib/cloudkeeper/aws/cloud.rb', line 9

def bucket
  @bucket
end

#ec2Object (readonly)

Returns the value of attribute ec2.



9
10
11
# File 'lib/cloudkeeper/aws/cloud.rb', line 9

def ec2
  @ec2
end

#s3Object (readonly)

Returns the value of attribute s3.



9
10
11
# File 'lib/cloudkeeper/aws/cloud.rb', line 9

def s3
  @s3
end

Instance Method Details

#delete_data(file_name) ⇒ Object



57
58
59
60
61
# File 'lib/cloudkeeper/aws/cloud.rb', line 57

def delete_data(file_name)
  logger.debug { "Deleting file: #{file_name} from bucket: #{Cloudkeeper::Aws::Settings['bucket-name']}" }
  obj = bucket.object(file_name)
  obj.exists? ? obj.delete : logger.info("File does not exist: #{file_name}")
end

#deregister_image(image_id) ⇒ Object

Note:

This method can be billed by AWS

Deregisters specific image.

Parameters:

  • image_id (String)

    id of specific AMI



139
140
141
142
# File 'lib/cloudkeeper/aws/cloud.rb', line 139

def deregister_image(image_id)
  logger.debug { "Deregistering AMI #{image_id}" }
  ec2.deregister_image(image_id: image_id)
end

#disk_container(appliance) ⇒ Hash

Method used for generating disk container for import image task

Parameters:

  • appliance (Appliance)

    data about image

Returns:

  • (Hash)

    disk container hash



81
82
83
84
85
86
87
88
89
90
# File 'lib/cloudkeeper/aws/cloud.rb', line 81

def disk_container(appliance)
  {
    description: appliance.description,
    format: appliance.image.format,
    user_bucket: {
      s3_bucket: @bucket.name,
      s3_key: appliance.identifier
    }
  }
end

#find_appliance(identifier) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
# File 'lib/cloudkeeper/aws/cloud.rb', line 160

def find_appliance(identifier)
  logger.debug { "Fetching appliance with identifier: #{identifier}" }
  images = ec2.describe_images(filters: FilterHelper.appliance(identifier)).images
  raise Cloudkeeper::Aws::Errors::Backend::ApplianceNotFoundError, 'Appliance not found' if images.empty?

  if images.size > 1
    raise Cloudkeeper::Aws::Errors::Backend::MultipleAppliancesFoundError,
          'Multiple appliances with same identifier exist in AWS'
  end
  images.first
end

#poll_import_task(import_id) ⇒ Object

Note:

This method can be billed by AWS

Polls for import image task result. This method is blocking, so after image import task is completed, successfully or not, it will return true or false.

Parameters:

  • import_id (String)

    id of import image task

Raises:

  • (Cloudkeeper::Aws::Errors::BackendError)

    if polling timed out



99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/cloudkeeper/aws/cloud.rb', line 99

def poll_import_task(import_id)
  logger.debug { "Polling for import task #{import_id}" }
  timeout do
    sleep_loop do
      import_task = ec2.describe_import_image_tasks(import_task_ids: [import_id]).import_image_tasks.first
      print_progress(import_task)
      if UNSUCCESSFUL_STATUS.include?(import_task.status)
        raise Cloudkeeper::Aws::Errors::Backend::ImageImportError,
              "Import failed with status #{import_task.status} and message: #{import_task.status_message}"
      end
      return import_task.image_id if SUCCESSFUL_STATUS.include?(import_task.status)
    end
  end
end


114
115
116
117
# File 'lib/cloudkeeper/aws/cloud.rb', line 114

def print_progress(import_task)
  logger.info "Import ##{import_task.import_task_id} [#{import_task.status}] with progress #{import_task.progress}%" \
    if Cloudkeeper::Aws::Settings['progress']
end

#search_images(filters) ⇒ Object



155
156
157
158
# File 'lib/cloudkeeper/aws/cloud.rb', line 155

def search_images(filters)
  logger.debug { "Searching for AMI with filters: #{filters}" }
  ec2.describe_images(filters: filters).images
end

#set_tags(tags, image_id) ⇒ Object

Note:

This method can be billed by AWS

Sets tags to specific AMI.

Parameters:

  • tags (Array<Hash{Symbol => String}>)

    array of tags to set to specific AMI. Tag consists of key and value symbols

  • image_id (String)

    id of specific AMI



150
151
152
153
# File 'lib/cloudkeeper/aws/cloud.rb', line 150

def set_tags(tags, image_id)
  logger.debug { "Setting tags for AMI #{image_id}: #{tags}" }
  ec2.create_tags(resources: [image_id], tags: tags)
end

#sleep_loopObject

Simple method used for calling block in intervals



120
121
122
123
124
125
# File 'lib/cloudkeeper/aws/cloud.rb', line 120

def sleep_loop
  loop do
    sleep Cloudkeeper::Aws::Settings['polling-interval']
    yield
  end
end

#start_import_image(appliance) ⇒ Number

Note:

This method can be billed by AWS

Creates import image task on AWS cloud. This task needs to be polled for. See #poll_import_task.

Parameters:

  • appliance (Appliance)

    data about image

Returns:

  • (Number)

    import task id



69
70
71
72
73
74
75
# File 'lib/cloudkeeper/aws/cloud.rb', line 69

def start_import_image(appliance)
  logger.debug { "Starting import image task for #{appliance.identifier}" }
  ec2.import_image(
    description: appliance.description,
    disk_containers: [disk_container(appliance)]
  ).import_task_id
end

#timeoutObject

Simple method used for handling timeout



128
129
130
131
132
133
# File 'lib/cloudkeeper/aws/cloud.rb', line 128

def timeout
  Timeout.timeout(Cloudkeeper::Aws::Settings['polling-timeout'],
                  Cloudkeeper::Aws::Errors::Backend::TimeoutError) do
    yield
  end
end

#upload_data(file_name) {|write_stream| ... } ⇒ Object

Note:

This method can be billed by AWS

Uploads data in block AWS file with given name

Parameters:

  • file_name (String)

    key of object in bucket

Yields:

  • (write_stream)

    output stream

Raises:

  • (Cloudkeeper::Aws::Errors::BackendError)

    if file already exists



31
32
33
34
35
36
37
38
39
# File 'lib/cloudkeeper/aws/cloud.rb', line 31

def upload_data(file_name, &block)
  logger.debug { "Block uploading to entry (#{file_name}) in bucket(#{Cloudkeeper::Aws::Settings['bucket-name']})" }
  obj = bucket.object(file_name)
  if obj.exists?
    raise Cloudkeeper::Aws::Errors::Backend::BackendError,
          "File #{file_name} in AWS bucket already exists"
  end
  obj.upload_stream(&block)
end

#upload_file(file_name, file_path) ⇒ Object

Note:

This method can be billed by AWS

Uploads file to AWS bucket

Parameters:

  • file_name (String)

    name of file in AWS bucket

  • file_path (String)

    name of file on local machine

Raises:

  • (Cloudkeeper::Aws::Errors::BackendError)

    if file already exists



47
48
49
50
51
52
53
54
55
# File 'lib/cloudkeeper/aws/cloud.rb', line 47

def upload_file(file_name, file_path)
  logger.debug { "Local file uploading to entry (#{file_name}) in bucket(#{Cloudkeeper::Aws::Settings['bucket-name']})" }
  obj = bucket.object(file_name)
  if obj.exists?
    raise Cloudkeeper::Aws::Errors::Backend::BackendError,
          "File #{file_name} in AWS bucket already exists"
  end
  obj.upload_file(file_path)
end