1
0
Fork 0
mirror of https://github.com/fog/fog-aws.git synced 2022-11-09 13:50:52 -05:00

Merge pull request #350 from engineyard/modify-volume

Modify volume
This commit is contained in:
Josh Lane 2017-03-09 14:02:47 -08:00 committed by GitHub
commit 4c04799d24
8 changed files with 339 additions and 25 deletions

View file

@ -120,6 +120,7 @@ module Fog
request :describe_subnets
request :describe_tags
request :describe_volumes
request :describe_volumes_modifications
request :describe_volume_status
request :describe_vpcs
request :describe_vpc_attribute
@ -140,6 +141,7 @@ module Fog
request :modify_network_interface_attribute
request :modify_snapshot_attribute
request :modify_subnet_attribute
request :modify_volume
request :modify_volume_attribute
request :modify_vpc_attribute
request :move_address_to_vpc
@ -290,6 +292,7 @@ module Fog
}
],
:spot_requests => {},
:volume_modifications => {}
}
end
end

View file

@ -36,31 +36,50 @@ module Fog
state == 'available'
end
def modification_in_progress?
modifications.any? { |m| m['modificationState'] != 'completed' }
end
def modifications
requires :identity
service.describe_volumes_modifications('volume-id' => self.identity).body['volumeModificationSet']
end
def save
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
requires :availability_zone
requires_one :size, :snapshot_id
if identity
update_params = {
'Size' => self.size,
'Iops' => self.iops,
'VolumeType' => self.type
}
if type == 'io1'
requires :iops
service.modify_volume(self.identity, update_params)
true
else
requires :availability_zone
requires_one :size, :snapshot_id
if type == 'io1'
requires :iops
end
data = service.create_volume(availability_zone, size, create_params).body
merge_attributes(data)
if tags = self.tags
# expect eventual consistency
Fog.wait_for { self.reload rescue nil }
service.create_tags(
self.identity,
tags
)
end
if @server
self.server = @server
end
true
end
data = service.create_volume(availability_zone, size, create_params).body
merge_attributes(data)
if tags = self.tags
# expect eventual consistency
Fog.wait_for { self.reload rescue nil }
service.create_tags(
self.identity,
tags
)
end
if @server
self.server = @server
end
true
end
def server

View file

@ -0,0 +1,30 @@
module Fog
module Parsers
module Compute
module AWS
class DescribeVolumesModifications < Fog::Parsers::Base
def reset
@response = { 'volumeModificationSet' => [] }
@modification = {}
end
def end_element(name)
case name
when 'modificationState', 'originalVolumeType', 'statusMessage', 'targetVolumeType', 'volumeId'
@modification[name] = value
when 'startTime', 'endTime'
@modification[name] = Time.parse(value)
when 'originalIops', 'originalSize', 'progress', 'targetIops', 'targetSize'
@modification[name] = value.to_i
when 'requestId'
@response[name] = value
when 'item'
@response['volumeModificationSet'] << @modification.dup
@modification = {}
end
end
end
end
end
end
end

View file

@ -0,0 +1,26 @@
module Fog
module Parsers
module Compute
module AWS
class ModifyVolume < Fog::Parsers::Base
def reset
@response = {'volumeModification' => {}}
end
def end_element(name)
case name
when 'modificationState', 'originalVolumeType', 'statusMessage', 'targetVolumeType', 'volumeId'
@response['volumeModification'][name] = value
when 'startTime', 'endTime'
@response['volumeModification'][name] = Time.parse(value)
when 'originalIops', 'originalSize', 'progress', 'targetIops', 'targetSize'
@response['volumeModification'][name] = value.to_i
when 'requestId'
@response[name] = value
end
end
end
end
end
end
end

View file

@ -0,0 +1,93 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/describe_volumes_modifications'
# Reports the current modification status of EBS volumes.
#
# ==== Parameters
# * filters<~Hash> - List of filters to limit results with
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>
# * 'volumeModificationSet'<~Array>:
# * 'targetIops'<~Integer> - Target IOPS rate of the volume being modified.
# * 'originalIops'<~Integer> - Original IOPS rate of the volume being modified.
# * 'modificationState'<~String> - Current state of modification. Modification state is null for unmodified volumes.
# * 'targetSize'<~Integer> - Target size of the volume being modified.
# * 'targetVolumeType'<~String> - Target EBS volume type of the volume being modified.
# * 'volumeId'<~String> - ID of the volume being modified.
# * 'progress'<~Integer> - Modification progress from 0 to 100%.
# * 'startTime'<~Time> - Modification start time
# * 'endTime'<~Time> - Modification end time
# * 'originalSize'<~Integer> - Original size of the volume being modified.
# * 'originalVolumeType'<~String> - Original EBS volume type of the volume being modified.
def describe_volumes_modifications(filters = {})
params = {}
if volume_id = filters.delete('volume-id')
params.merge!(Fog::AWS.indexed_param('VolumeId.%d', [*volume_id]))
end
params.merge!(Fog::AWS.indexed_filters(filters))
request({
'Action' => 'DescribeVolumesModifications',
:idempotent => true,
:parser => Fog::Parsers::Compute::AWS::DescribeVolumesModifications.new
}.merge(params))
end
end
class Mock
def describe_volumes_modifications(filters = {})
response = Excon::Response.new
modification_set = self.data[:volume_modifications].values
aliases = {
'volume-id' => 'volumeId',
'modification-state' => 'modificationState',
'target-size' => 'targetSize',
'target-iops' => 'targetIops',
'target-volume-type' => 'targetVolumeType',
'original-size' => 'originalSize',
'original-iops' => 'originalIops',
'original-volume-type' => 'originalVolumeType',
'start-time' => 'startTime'
}
attribute_aliases = {
'targetSize' => 'size',
'targetVolumeType' => 'volumeType',
'targetIops' => 'iops'
}
for filter_key, filter_value in filters
aliased_key = aliases[filter_key]
modification_set = modification_set.reject { |m| ![*filter_value].include?(m[aliased_key]) }
end
modification_set.each do |modification|
case modification['modificationState']
when 'modifying'
volume = self.data[:volumes][modification['volumeId']]
modification['modificationState'] = 'optimizing'
%w(targetSize targetIops targetVolumeType).each do |attribute|
aliased_attribute = attribute_aliases[attribute]
volume[aliased_attribute] = modification[attribute] if modification[attribute]
end
self.data[:volumes][modification['volumeId']] = volume
when 'optimizing'
modification['modificationState'] = 'completed'
modification['endTime'] = Time.now
end
end
response.body = {'requestId' => Fog::AWS::Mock.request_id, 'volumeModificationSet' => modification_set}
response
end
end
end
end
end

View file

@ -0,0 +1,88 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/modify_volume'
# Modifies a volume
#
# ==== Parameters
# * volume_id<~String> - The ID of the volume
# * options<~Hash>:
# * 'VolumeType'<~String> - Type of volume
# * 'Size'<~Integer> - Size in GiBs fo the volume
# * 'Iops'<~Integer> - Number of IOPS the volume supports
#
# ==== Response
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'targetIops'<~Integer> - Target IOPS rate of the volume being modified.
# * 'originalIops'<~Integer> - Original IOPS rate of the volume being modified.
# * 'modificationState'<~String> - Current state of modification. Modification state is null for unmodified volumes.
# * 'targetSize'<~Integer> - Target size of the volume being modified.
# * 'targetVolumeType'<~String> - Target EBS volume type of the volume being modified.
# * 'volumeId'<~String> - ID of the volume being modified.
# * 'progress'<~Integer> - Modification progress from 0 to 100%.
# * 'startTime'<~Time> - Modification start time
# * 'endTime'<~Time> - Modification end time
# * 'originalSize'<~Integer> - Original size of the volume being modified.
# * 'originalVolumeType'<~String> - Original EBS volume type of the volume being modified.
def modify_volume(volume_id, options={})
request({
'Action' => "ModifyVolume",
'VolumeId' => volume_id,
:parser => Fog::Parsers::Compute::AWS::ModifyVolume.new
}.merge(options))
end
end
class Mock
def modify_volume(volume_id, options={})
response = Excon::Response.new
volume = self.data[:volumes][volume_id]
if volume["volumeType"] == 'standard' && options['VolumeType']
raise Fog::Compute::AWS::Error.new("InvalidParameterValue => Volume type EBS Magnetic is not supported.")
end
volume_modification = {
'modificationState' => 'modifying',
'progress' => 0,
'startTime' => Time.now,
'volumeId' => volume_id
}
if options['Size']
volume_modification.merge!(
'originalSize' => volume['size'],
'targetSize' => options['Size']
)
end
if options['Iops']
volume_modification.merge!(
'originalIops' => volume['iops'],
'targetIops' => options['Iops']
)
end
if options['VolumeType']
if options["VolumeType"] == 'standard'
raise Fog::Compute::AWS::Error.new("InvalidParameterValue => Volume type EBS Magnetic is not supported.")
end
volume_modification.merge!(
'originalVolumeType' => volume['volumeType'],
'targetVolumeType' => options['VolumeType']
)
end
self.data[:volume_modifications][volume_id] = volume_modification
response.body = {'volumeModification' => volume_modification, 'requestId' => Fog::AWS::Mock.request_id}
response
end
end
end
end
end

View file

@ -3,7 +3,7 @@ Shindo.tests("Fog::Compute[:aws] | volume", ['aws']) do
@server = Fog::Compute[:aws].servers.create
@server.wait_for { ready? }
model_tests(Fog::Compute[:aws].volumes, {:availability_zone => @server.availability_zone, :size => 1, :device => '/dev/sdz1', :tags => {"key" => "value"}}, true) do
model_tests(Fog::Compute[:aws].volumes, {:availability_zone => @server.availability_zone, :size => 1, :device => '/dev/sdz1', :tags => {"key" => "value"}, :type => 'gp2'}, true) do
@instance.wait_for { ready? }
@ -32,6 +32,21 @@ Shindo.tests("Fog::Compute[:aws] | volume", ['aws']) do
@instance.wait_for { ready? }
@instance.type = 'io1'
@instance.iops = 5000
@instance.size = 100
@instance.save
returns(true) { @instance.modification_in_progress? }
@instance.wait_for { !modification_in_progress? }
# avoid weirdness with merge_attributes
@instance = Fog::Compute[:aws].volumes.get(@instance.identity)
returns('io1') { @instance.type }
returns(5000) { @instance.iops }
returns(100) { @instance.size }
tests('@instance.reload.tags').returns({'key' => 'value'}) do
@instance.reload.tags
end

View file

@ -68,6 +68,29 @@ Shindo.tests('Fog::Compute[:aws] | volume requests', ['aws']) do
'requestId' => String
}
@volume_modification_format = {
'endTime' => Fog::Nullable::Time,
'modificationState' => String,
'originalIops' => Fog::Nullable::Integer,
'originalSize' => Fog::Nullable::Integer,
'originalVolumeType' => Fog::Nullable::String,
'startTime' => Time,
'targetIops' => Fog::Nullable::Integer,
'targetSize' => Fog::Nullable::Integer,
'targetVolumeType' => Fog::Nullable::String,
'volumeId' => String,
}
@modify_volume_format = {
'requestId' => String,
'volumeModification' => @volume_modification_format
}
@describe_volume_modifications_format = {
'requestId' => String,
'volumeModificationSet' => [@volume_modification_format]
}
@server = Fog::Compute[:aws].servers.create
@server.wait_for { ready? }
@ -113,13 +136,13 @@ Shindo.tests('Fog::Compute[:aws] | volume requests', ['aws']) do
Fog::Compute[:aws].delete_volume(@volume_id)
tests('#create_volume from snapshot with size').formats(@volume_format) do
volume = Fog::Compute[:aws].volumes.create(:availability_zone => 'us-east-1d', :size => 1)
volume = Fog::Compute[:aws].volumes.create(:availability_zone => 'us-east-1d', :size => 1, :type => 'gp2')
volume.wait_for { ready? }
snapshot = Fog::Compute[:aws].create_snapshot(volume.identity).body
Fog::Compute[:aws].snapshots.new(snapshot).wait_for { ready? }
data = Fog::Compute[:aws].create_volume(@server.availability_zone, 1, 'SnapshotId' => snapshot['snapshotId']).body
data = Fog::Compute[:aws].create_volume(@server.availability_zone, 1, 'SnapshotId' => snapshot['snapshotId'], 'VolumeType' => 'gp2').body
@volume_id = data['volumeId']
data
end
@ -155,6 +178,23 @@ Shindo.tests('Fog::Compute[:aws] | volume requests', ['aws']) do
Fog::Compute[:aws].volumes.get(@volume_id).wait_for { ready? }
tests("#modify_volume('#{@volume_id}', 'Size' => 100, 'VolumeType' => 'io1', 'Iops' => 5000").formats(@modify_volume_format) do
Fog::Compute[:aws].modify_volume(@volume_id, 'Size' => 100, 'VolumeType' => 'io1', 'Iops' => 5000).body
end
tests("#describe_volumes_modifications('volume-id' => '#{@volume_id}')").formats(@describe_volume_modifications_format) do
Fog.wait_for do
Fog::Compute[:aws].describe_volumes_modifications('volume-id' => @volume_id).body['volumeModificationSet'].first['modificationState'] == 'completed'
end
volume = Fog::Compute[:aws].describe_volumes('volume-id' => @volume_id).body['volumeSet'].first
returns(100) { volume['size'] }
returns('io1') { volume['volumeType'] }
returns(5000) { volume['iops'] }
Fog::Compute[:aws].describe_volumes_modifications('volume-id' => @volume_id).body
end
tests("#modify_volume_attribute('#{@volume_id}', true)").formats(AWS::Compute::Formats::BASIC) do
Fog::Compute[:aws].modify_volume_attribute(@volume_id, true).body
end