From 32ee4dd7cfcf229a665d859146769a49ca5440af Mon Sep 17 00:00:00 2001 From: Virendra Negi Date: Thu, 16 Jan 2025 11:10:09 +0530 Subject: [PATCH] add support for reserved ipv6 api --- README.md | 22 ++ lib/droplet_kit.rb | 4 + lib/droplet_kit/client.rb | 2 + .../mappings/reserved_ipv6_mapping.rb | 17 ++ lib/droplet_kit/models/reserved_ipv6.rb | 19 ++ .../reserved_ipv6_action_resource.rb | 20 ++ .../resources/reserved_ipv6_resource.rb | 32 ++ spec/fixtures/reserved_ipv6/all.json | 283 ++++++++++++++++++ spec/fixtures/reserved_ipv6/all_empty.json | 7 + spec/fixtures/reserved_ipv6/create.json | 7 + spec/fixtures/reserved_ipv6/find.json | 272 +++++++++++++++++ .../reserved_ipv6_actions/assign.json | 13 + .../reserved_ipv6_actions/unassign.json | 13 + .../reserved_ipv6_action_resource_spec.rb | 84 ++++++ .../resources/reserved_ipv6_resource_spec.rb | 89 ++++++ .../shared_examples/bad_request_error.rb | 13 + 16 files changed, 897 insertions(+) create mode 100644 lib/droplet_kit/mappings/reserved_ipv6_mapping.rb create mode 100644 lib/droplet_kit/models/reserved_ipv6.rb create mode 100644 lib/droplet_kit/resources/reserved_ipv6_action_resource.rb create mode 100644 lib/droplet_kit/resources/reserved_ipv6_resource.rb create mode 100644 spec/fixtures/reserved_ipv6/all.json create mode 100644 spec/fixtures/reserved_ipv6/all_empty.json create mode 100644 spec/fixtures/reserved_ipv6/create.json create mode 100644 spec/fixtures/reserved_ipv6/find.json create mode 100644 spec/fixtures/reserved_ipv6_actions/assign.json create mode 100644 spec/fixtures/reserved_ipv6_actions/unassign.json create mode 100644 spec/lib/droplet_kit/resources/reserved_ipv6_action_resource_spec.rb create mode 100644 spec/lib/droplet_kit/resources/reserved_ipv6_resource_spec.rb create mode 100644 spec/support/shared_examples/bad_request_error.rb diff --git a/README.md b/README.md index e956a99c..8519af90 100644 --- a/README.md +++ b/README.md @@ -585,6 +585,28 @@ Actions supported: * `client.reserved_ip_actions.assign(ip: reserved_ip.ip, droplet_id: droplet.id)` * `client.reserved_ip_actions.unassign(ip: reserved_ip.ip)` +## Reserved IPv6 resource + + client = DropletKit::Client.new(access_token: 'TOKEN') + client.reserved_ipv6s #=> DropletKit::ReservedIpv6Resource + +Actions supported: + +* `client.reserved_ipv6s.all()` +* `client.reserved_ipv6s.find(ip: 'ip address')` +* `client.reserved_ipv6s.create(reserved_ipv6)` +* `client.reserved_ipv6s.delete(ip: 'ip address')` + +## Reserved IPv6 Action resource + + client = DropletKit::Client.new(access_token: 'TOKEN') + client.reserved_ipv6_actions #=> DropletKit::ReservedIpv6ActionResource + +Actions supported: + +* `client.reserved_ipv6_actions.assign(ip: reserved_ipv6.ip, droplet_id: droplet.id)` +* `client.reserved_ipv6_actions.unassign(ip: reserved_ipv6.ip)` + ## Volume resource client = DropletKit::Client.new(access_token: 'TOKEN') diff --git a/lib/droplet_kit.rb b/lib/droplet_kit.rb index c645ad42..3847b681 100644 --- a/lib/droplet_kit.rb +++ b/lib/droplet_kit.rb @@ -31,6 +31,7 @@ module DropletKit autoload :DropletUpgrade, 'droplet_kit/models/droplet_upgrade' autoload :FloatingIp, 'droplet_kit/models/floating_ip' autoload :ReservedIp, 'droplet_kit/models/reserved_ip' + autoload :ReservedIpv6, 'droplet_kit/models/reserved_ipv6' autoload :Project, 'droplet_kit/models/project' autoload :ProjectAssignment, 'droplet_kit/models/project_assignment' autoload :Links, 'droplet_kit/models/links' @@ -155,7 +156,9 @@ module DropletKit autoload :FloatingIpResource, 'droplet_kit/resources/floating_ip_resource' autoload :FloatingIpActionResource, 'droplet_kit/resources/floating_ip_action_resource' autoload :ReservedIpResource, 'droplet_kit/resources/reserved_ip_resource' + autoload :ReservedIpv6Resource, 'droplet_kit/resources/reserved_ipv6_resource' autoload :ReservedIpActionResource, 'droplet_kit/resources/reserved_ip_action_resource' + autoload :ReservedIpv6ActionResource, 'droplet_kit/resources/reserved_ipv6_action_resource' autoload :ProjectResource, 'droplet_kit/resources/project_resource' autoload :TagResource, 'droplet_kit/resources/tag_resource' autoload :VolumeResource, 'droplet_kit/resources/volume_resource' @@ -197,6 +200,7 @@ module DropletKit autoload :DropletUpgradeMapping, 'droplet_kit/mappings/droplet_upgrade_mapping' autoload :FloatingIpMapping, 'droplet_kit/mappings/floating_ip_mapping' autoload :ReservedIpMapping, 'droplet_kit/mappings/reserved_ip_mapping' + autoload :ReservedIpv6Mapping, 'droplet_kit/mappings/reserved_ipv6_mapping' autoload :ProjectMapping, 'droplet_kit/mappings/project_mapping' autoload :ProjectAssignmentMapping, 'droplet_kit/mappings/project_assignment_mapping' autoload :LinksMapping, 'droplet_kit/mappings/links_mapping' diff --git a/lib/droplet_kit/client.rb b/lib/droplet_kit/client.rb index 04d4e324..7c6b9c76 100644 --- a/lib/droplet_kit/client.rb +++ b/lib/droplet_kit/client.rb @@ -75,7 +75,9 @@ def self.resources floating_ips: FloatingIpResource, floating_ip_actions: FloatingIpActionResource, reserved_ips: ReservedIpResource, + reserved_ipv6s: ReservedIpv6Resource, reserved_ip_actions: ReservedIpActionResource, + reserved_ipv6_actions: ReservedIpv6ActionResource, tags: TagResource, projects: ProjectResource, volumes: VolumeResource, diff --git a/lib/droplet_kit/mappings/reserved_ipv6_mapping.rb b/lib/droplet_kit/mappings/reserved_ipv6_mapping.rb new file mode 100644 index 00000000..6ac9bbdc --- /dev/null +++ b/lib/droplet_kit/mappings/reserved_ipv6_mapping.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module DropletKit + class ReservedIpv6Mapping + include Kartograph::DSL + + kartograph do + mapping ReservedIpv6 + root_key plural: 'reserved_ipv6s', singular: 'reserved_ipv6', scopes: [:read] + + property :ip, scopes: [:read] + property :region_slug, scopes: [:read, :create] + property :droplet, scopes: [:read], include: DropletMapping + property :reserved_at, scopes: [:read] + end + end +end diff --git a/lib/droplet_kit/models/reserved_ipv6.rb b/lib/droplet_kit/models/reserved_ipv6.rb new file mode 100644 index 00000000..e88da6d0 --- /dev/null +++ b/lib/droplet_kit/models/reserved_ipv6.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module DropletKit + class ReservedIpv6 < BaseModel + attribute :ip + attribute :droplet + attribute :reserved_at + # Used for creates + attribute :region_slug + + def identifier + ip + end + + def self.from_identifier(identifier) + new(ip: identifier) + end + end +end diff --git a/lib/droplet_kit/resources/reserved_ipv6_action_resource.rb b/lib/droplet_kit/resources/reserved_ipv6_action_resource.rb new file mode 100644 index 00000000..b020c028 --- /dev/null +++ b/lib/droplet_kit/resources/reserved_ipv6_action_resource.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module DropletKit + class ReservedIpv6ActionResource < ResourceKit::Resource + resources do + default_handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) } + default_handler(400) { |response| ErrorMapping.fail_with(FailedCreate, response.body) } + + action :assign, 'POST /v2/reserved_ipv6/:ip/actions' do + body { |hash| { type: 'assign', droplet_id: hash[:droplet_id] }.to_json } + handler(201, 200) { |response| ActionMapping.extract_single(response.body, :read) } + end + + action :unassign, 'POST /v2/reserved_ipv6/:ip/actions' do + body { |hash| { type: 'unassign' }.to_json } + handler(201, 200) { |response| ActionMapping.extract_single(response.body, :read) } + end + end + end +end diff --git a/lib/droplet_kit/resources/reserved_ipv6_resource.rb b/lib/droplet_kit/resources/reserved_ipv6_resource.rb new file mode 100644 index 00000000..e411446e --- /dev/null +++ b/lib/droplet_kit/resources/reserved_ipv6_resource.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +module DropletKit + class ReservedIpv6Resource < ResourceKit::Resource + resources do + action :all, 'GET /v2/reserved_ipv6' do + query_keys :per_page, :page + handler(200) { |response| ReservedIpv6Mapping.extract_collection(response.body, :read) } + end + + action :find, 'GET /v2/reserved_ipv6/:ip' do + handler(200) { |response| ReservedIpv6Mapping.extract_single(response.body, :read) } + handler(404) { |response| ErrorMapping.fail_with(Error, response.body) } + end + + action :create, 'POST /v2/reserved_ipv6' do + body { |object| ReservedIpv6Mapping.representation_for(:create, object) } + handler(201) { |response| ReservedIpv6Mapping.extract_single(response.body, :read) } + handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) } + end + + action :delete, 'DELETE /v2/reserved_ipv6/:ip' do + handler(404) { |response| ErrorMapping.fail_with(FailedDelete, response.body) } + handler(202) { |response| ActionMapping.extract_single(response.body, :read) } + handler(204) { |response| true } + end + end + + def all(*args) + PaginatedResource.new(action(:all), self, *args) + end + end +end diff --git a/spec/fixtures/reserved_ipv6/all.json b/spec/fixtures/reserved_ipv6/all.json new file mode 100644 index 00000000..67e4e527 --- /dev/null +++ b/spec/fixtures/reserved_ipv6/all.json @@ -0,0 +1,283 @@ +{ + "reserved_ipv6s": [ + { + "ip": "2a03:b0c0:3:f0::5dcf:9000", + "region_slug": "fra1", + "droplet": { + "id": 469950602, + "name": "werain-fra-1", + "memory": 512, + "vcpus": 1, + "disk": 10, + "disk_info": [ + { + "type": "local", + "size": { + "amount": 10, + "unit": "gib" + } + } + ], + "locked": false, + "status": "off", + "kernel": null, + "created_at": "2025-01-15T11:03:58Z", + "features": [ + "droplet_agent", + "ipv6", + "private_networking" + ], + "backup_ids": [], + "next_backup_window": null, + "snapshot_ids": [], + "image": { + "id": 168977420, + "name": "24.10 x64", + "distribution": "Ubuntu", + "slug": "ubuntu-24-10-x64", + "public": true, + "regions": [ + "nyc3", + "nyc1", + "sfo1", + "nyc2", + "ams2", + "sgp1", + "lon1", + "ams3", + "fra1", + "tor1", + "sfo2", + "blr1", + "sfo3", + "syd1" + ], + "created_at": "2024-10-28T21:26:20Z", + "min_disk_size": 7, + "type": "base", + "size_gigabytes": 3.76, + "description": "Ubuntu 24.10 x64", + "tags": [], + "status": "available" + }, + "volume_ids": [], + "size": { + "slug": "s-1vcpu-512mb-10gb", + "memory": 512, + "vcpus": 1, + "disk": 10, + "transfer": 0.5, + "price_monthly": 4.0, + "price_hourly": 0.00595, + "regions": [ + "ams3", + "fra1", + "nyc1", + "sfo3", + "sgp1", + "syd1" + ], + "available": true, + "description": "Basic", + "networking_throughput": 2000, + "disk_info": [ + { + "type": "local", + "size": { + "amount": 10, + "unit": "gib" + } + } + ] + }, + "size_slug": "s-1vcpu-512mb-10gb", + "networks": { + "v4": [ + { + "ip_address": "165.227.163.159", + "netmask": "255.255.240.0", + "gateway": "165.227.160.1", + "type": "public" + }, + { + "ip_address": "10.114.0.3", + "netmask": "255.255.240.0", + "gateway": "10.114.0.1", + "type": "private" + } + ], + "v6": [ + { + "ip_address": "2a03:b0c0:3:f0::5dcf:9000", + "netmask": 0, + "gateway": "2a03:b0c0:3:f0::1", + "type": "public" + } + ] + }, + "region": { + "name": "Frankfurt 1", + "slug": "fra1", + "features": [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer" + ], + "available": true, + "sizes": [ + "s-1vcpu-512mb-10gb", + "s-1vcpu-1gb", + "s-1vcpu-1gb-amd", + "s-1vcpu-1gb-intel", + "s-1vcpu-1gb-35gb-intel", + "s-1vcpu-2gb", + "s-1vcpu-2gb-amd", + "s-1vcpu-2gb-intel", + "s-1vcpu-2gb-70gb-intel", + "s-2vcpu-2gb", + "s-2vcpu-2gb-amd", + "s-2vcpu-2gb-intel", + "s-2vcpu-2gb-90gb-intel", + "s-2vcpu-4gb", + "s-2vcpu-4gb-amd", + "s-2vcpu-4gb-intel", + "s-2vcpu-4gb-120gb-intel", + "s-2vcpu-8gb-amd", + "c-2", + "c2-2vcpu-4gb", + "s-2vcpu-8gb-160gb-intel", + "s-4vcpu-8gb", + "s-4vcpu-8gb-amd", + "s-4vcpu-8gb-intel", + "g-2vcpu-8gb", + "s-4vcpu-8gb-240gb-intel", + "gd-2vcpu-8gb", + "g-2vcpu-8gb-intel", + "gd-2vcpu-8gb-intel", + "s-4vcpu-16gb-amd", + "m-2vcpu-16gb", + "c-4", + "c2-4vcpu-8gb", + "s-4vcpu-16gb-320gb-intel", + "s-8vcpu-16gb", + "m-2vcpu-16gb-intel", + "m3-2vcpu-16gb", + "c-4-intel", + "m3-2vcpu-16gb-intel", + "s-8vcpu-16gb-amd", + "s-8vcpu-16gb-intel", + "c2-4vcpu-8gb-intel", + "g-4vcpu-16gb", + "s-8vcpu-16gb-480gb-intel", + "so-2vcpu-16gb-intel", + "so-2vcpu-16gb", + "m6-2vcpu-16gb", + "gd-4vcpu-16gb", + "so1_5-2vcpu-16gb-intel", + "g-4vcpu-16gb-intel", + "gd-4vcpu-16gb-intel", + "so1_5-2vcpu-16gb", + "s-8vcpu-32gb-amd", + "m-4vcpu-32gb", + "c-8", + "c2-8vcpu-16gb", + "s-8vcpu-32gb-640gb-intel", + "m-4vcpu-32gb-intel", + "m3-4vcpu-32gb", + "c-8-intel", + "m3-4vcpu-32gb-intel", + "c2-8vcpu-16gb-intel", + "g-8vcpu-32gb", + "so-4vcpu-32gb-intel", + "so-4vcpu-32gb", + "m6-4vcpu-32gb", + "gd-8vcpu-32gb", + "so1_5-4vcpu-32gb-intel", + "g-8vcpu-32gb-intel", + "gd-8vcpu-32gb-intel", + "so1_5-4vcpu-32gb", + "m-8vcpu-64gb", + "c-16", + "c2-16vcpu-32gb", + "m-8vcpu-64gb-intel", + "m3-8vcpu-64gb", + "c-16-intel", + "m3-8vcpu-64gb-intel", + "c2-16vcpu-32gb-intel", + "g-16vcpu-64gb", + "so-8vcpu-64gb-intel", + "so-8vcpu-64gb", + "m6-8vcpu-64gb", + "gd-16vcpu-64gb", + "so1_5-8vcpu-64gb-intel", + "g-16vcpu-64gb-intel", + "gd-16vcpu-64gb-intel", + "so1_5-8vcpu-64gb", + "m-16vcpu-128gb", + "c-32", + "c2-32vcpu-64gb", + "m-16vcpu-128gb-intel", + "m3-16vcpu-128gb", + "c-32-intel", + "m3-16vcpu-128gb-intel", + "c2-32vcpu-64gb-intel", + "c-48", + "m-24vcpu-192gb", + "g-32vcpu-128gb", + "so-16vcpu-128gb-intel", + "so-16vcpu-128gb", + "m6-16vcpu-128gb", + "gd-32vcpu-128gb", + "so1_5-16vcpu-128gb-intel", + "c2-48vcpu-96gb", + "m-24vcpu-192gb-intel", + "g-32vcpu-128gb-intel", + "m3-24vcpu-192gb", + "g-40vcpu-160gb", + "gd-32vcpu-128gb-intel", + "so1_5-16vcpu-128gb", + "c-48-intel", + "m3-24vcpu-192gb-intel", + "gd-40vcpu-160gb", + "c2-48vcpu-96gb-intel", + "so-24vcpu-192gb-intel", + "so-24vcpu-192gb", + "m6-24vcpu-192gb", + "m-32vcpu-256gb-intel", + "c-60-intel", + "so1_5-24vcpu-192gb-intel", + "m3-32vcpu-256gb-intel", + "g-48vcpu-192gb-intel", + "c2-60vcpu-120gb-intel", + "gd-48vcpu-192gb-intel", + "so1_5-24vcpu-192gb", + "so-32vcpu-256gb-intel", + "so1_5-32vcpu-256gb-intel", + "g-60vcpu-240gb-intel", + "m-48vcpu-384gb-intel", + "gd-60vcpu-240gb-intel", + "m3-48vcpu-384gb-intel", + "so-48vcpu-384gb-intel" + ] + }, + "tags": [ + "fra-1" + ], + "vpc_uuid": "3c41cfc8-3732-4fe0-9010-53600c432634" + }, + "reserved_at": "2025-01-15T12:38:02Z" + }, + { + "ip": "2604:a880:800:14::79d4:4000", + "region_slug": "nyc3", + "reserved_at": "2025-01-15T12:31:00Z" + } + ], + "meta": { + "total": 2 + }, + "links": {} +} \ No newline at end of file diff --git a/spec/fixtures/reserved_ipv6/all_empty.json b/spec/fixtures/reserved_ipv6/all_empty.json new file mode 100644 index 00000000..6137051d --- /dev/null +++ b/spec/fixtures/reserved_ipv6/all_empty.json @@ -0,0 +1,7 @@ +{ + "reserved_ipv6s": [ + ], + "meta": { + "total": 0 + } +} \ No newline at end of file diff --git a/spec/fixtures/reserved_ipv6/create.json b/spec/fixtures/reserved_ipv6/create.json new file mode 100644 index 00000000..418a2937 --- /dev/null +++ b/spec/fixtures/reserved_ipv6/create.json @@ -0,0 +1,7 @@ +{ + "reserved_ipv6": { + "ip": "2604:a880:800:14::79d4:4000", + "region_slug": "nyc1", + "reserved_at": "2025-01-15T12:31:00Z" + } +} \ No newline at end of file diff --git a/spec/fixtures/reserved_ipv6/find.json b/spec/fixtures/reserved_ipv6/find.json new file mode 100644 index 00000000..9c44d91d --- /dev/null +++ b/spec/fixtures/reserved_ipv6/find.json @@ -0,0 +1,272 @@ +{ + "reserved_ipv6": { + "ip": "2a03:b0c0:3:f0::5dcf:9000", + "region_slug": "fra1", + "droplet": { + "id": 469950602, + "name": "werain-fra-1", + "memory": 512, + "vcpus": 1, + "disk": 10, + "disk_info": [ + { + "type": "local", + "size": { + "amount": 10, + "unit": "gib" + } + } + ], + "locked": false, + "status": "off", + "kernel": null, + "created_at": "2025-01-15T11:03:58Z", + "features": [ + "droplet_agent", + "ipv6", + "private_networking" + ], + "backup_ids": [], + "next_backup_window": null, + "snapshot_ids": [], + "image": { + "id": 168977420, + "name": "24.10 x64", + "distribution": "Ubuntu", + "slug": "ubuntu-24-10-x64", + "public": true, + "regions": [ + "nyc3", + "nyc1", + "sfo1", + "nyc2", + "ams2", + "sgp1", + "lon1", + "ams3", + "fra1", + "tor1", + "sfo2", + "blr1", + "sfo3", + "syd1" + ], + "created_at": "2024-10-28T21:26:20Z", + "min_disk_size": 7, + "type": "base", + "size_gigabytes": 3.76, + "description": "Ubuntu 24.10 x64", + "tags": [], + "status": "available" + }, + "volume_ids": [], + "size": { + "slug": "s-1vcpu-512mb-10gb", + "memory": 512, + "vcpus": 1, + "disk": 10, + "transfer": 0.5, + "price_monthly": 4.0, + "price_hourly": 0.00595, + "regions": [ + "ams3", + "fra1", + "nyc1", + "sfo3", + "sgp1", + "syd1" + ], + "available": true, + "description": "Basic", + "networking_throughput": 2000, + "disk_info": [ + { + "type": "local", + "size": { + "amount": 10, + "unit": "gib" + } + } + ] + }, + "size_slug": "s-1vcpu-512mb-10gb", + "networks": { + "v4": [ + { + "ip_address": "165.227.163.159", + "netmask": "255.255.240.0", + "gateway": "165.227.160.1", + "type": "public" + }, + { + "ip_address": "10.114.0.3", + "netmask": "255.255.240.0", + "gateway": "10.114.0.1", + "type": "private" + } + ], + "v6": [ + { + "ip_address": "2a03:b0c0:3:f0::5dcf:9000", + "netmask": 0, + "gateway": "2a03:b0c0:3:f0::1", + "type": "public" + } + ] + }, + "region": { + "name": "Frankfurt 1", + "slug": "fra1", + "features": [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer" + ], + "available": true, + "sizes": [ + "s-1vcpu-512mb-10gb", + "s-1vcpu-1gb", + "s-1vcpu-1gb-amd", + "s-1vcpu-1gb-intel", + "s-1vcpu-1gb-35gb-intel", + "s-1vcpu-2gb", + "s-1vcpu-2gb-amd", + "s-1vcpu-2gb-intel", + "s-1vcpu-2gb-70gb-intel", + "s-2vcpu-2gb", + "s-2vcpu-2gb-amd", + "s-2vcpu-2gb-intel", + "s-2vcpu-2gb-90gb-intel", + "s-2vcpu-4gb", + "s-2vcpu-4gb-amd", + "s-2vcpu-4gb-intel", + "s-2vcpu-4gb-120gb-intel", + "s-2vcpu-8gb-amd", + "c-2", + "c2-2vcpu-4gb", + "s-2vcpu-8gb-160gb-intel", + "s-4vcpu-8gb", + "s-4vcpu-8gb-amd", + "s-4vcpu-8gb-intel", + "g-2vcpu-8gb", + "s-4vcpu-8gb-240gb-intel", + "gd-2vcpu-8gb", + "g-2vcpu-8gb-intel", + "gd-2vcpu-8gb-intel", + "s-4vcpu-16gb-amd", + "m-2vcpu-16gb", + "c-4", + "c2-4vcpu-8gb", + "s-4vcpu-16gb-320gb-intel", + "s-8vcpu-16gb", + "m-2vcpu-16gb-intel", + "m3-2vcpu-16gb", + "c-4-intel", + "m3-2vcpu-16gb-intel", + "s-8vcpu-16gb-amd", + "s-8vcpu-16gb-intel", + "c2-4vcpu-8gb-intel", + "g-4vcpu-16gb", + "s-8vcpu-16gb-480gb-intel", + "so-2vcpu-16gb-intel", + "so-2vcpu-16gb", + "m6-2vcpu-16gb", + "gd-4vcpu-16gb", + "so1_5-2vcpu-16gb-intel", + "g-4vcpu-16gb-intel", + "gd-4vcpu-16gb-intel", + "so1_5-2vcpu-16gb", + "s-8vcpu-32gb-amd", + "m-4vcpu-32gb", + "c-8", + "c2-8vcpu-16gb", + "s-8vcpu-32gb-640gb-intel", + "m-4vcpu-32gb-intel", + "m3-4vcpu-32gb", + "c-8-intel", + "m3-4vcpu-32gb-intel", + "c2-8vcpu-16gb-intel", + "g-8vcpu-32gb", + "so-4vcpu-32gb-intel", + "so-4vcpu-32gb", + "m6-4vcpu-32gb", + "gd-8vcpu-32gb", + "so1_5-4vcpu-32gb-intel", + "g-8vcpu-32gb-intel", + "gd-8vcpu-32gb-intel", + "so1_5-4vcpu-32gb", + "m-8vcpu-64gb", + "c-16", + "c2-16vcpu-32gb", + "m-8vcpu-64gb-intel", + "m3-8vcpu-64gb", + "c-16-intel", + "m3-8vcpu-64gb-intel", + "c2-16vcpu-32gb-intel", + "g-16vcpu-64gb", + "so-8vcpu-64gb-intel", + "so-8vcpu-64gb", + "m6-8vcpu-64gb", + "gd-16vcpu-64gb", + "so1_5-8vcpu-64gb-intel", + "g-16vcpu-64gb-intel", + "gd-16vcpu-64gb-intel", + "so1_5-8vcpu-64gb", + "m-16vcpu-128gb", + "c-32", + "c2-32vcpu-64gb", + "m-16vcpu-128gb-intel", + "m3-16vcpu-128gb", + "c-32-intel", + "m3-16vcpu-128gb-intel", + "c2-32vcpu-64gb-intel", + "c-48", + "m-24vcpu-192gb", + "g-32vcpu-128gb", + "so-16vcpu-128gb-intel", + "so-16vcpu-128gb", + "m6-16vcpu-128gb", + "gd-32vcpu-128gb", + "so1_5-16vcpu-128gb-intel", + "c2-48vcpu-96gb", + "m-24vcpu-192gb-intel", + "g-32vcpu-128gb-intel", + "m3-24vcpu-192gb", + "g-40vcpu-160gb", + "gd-32vcpu-128gb-intel", + "so1_5-16vcpu-128gb", + "c-48-intel", + "m3-24vcpu-192gb-intel", + "gd-40vcpu-160gb", + "c2-48vcpu-96gb-intel", + "so-24vcpu-192gb-intel", + "so-24vcpu-192gb", + "m6-24vcpu-192gb", + "m-32vcpu-256gb-intel", + "c-60-intel", + "so1_5-24vcpu-192gb-intel", + "m3-32vcpu-256gb-intel", + "g-48vcpu-192gb-intel", + "c2-60vcpu-120gb-intel", + "gd-48vcpu-192gb-intel", + "so1_5-24vcpu-192gb", + "so-32vcpu-256gb-intel", + "so1_5-32vcpu-256gb-intel", + "g-60vcpu-240gb-intel", + "m-48vcpu-384gb-intel", + "gd-60vcpu-240gb-intel", + "m3-48vcpu-384gb-intel", + "so-48vcpu-384gb-intel" + ] + }, + "tags": [ + "fra-1" + ], + "vpc_uuid": "3c41cfc8-3732-4fe0-9010-53600c432634" + }, + "reserved_at": "2025-01-15T12:38:02Z" + } +} \ No newline at end of file diff --git a/spec/fixtures/reserved_ipv6_actions/assign.json b/spec/fixtures/reserved_ipv6_actions/assign.json new file mode 100644 index 00000000..fb56a30b --- /dev/null +++ b/spec/fixtures/reserved_ipv6_actions/assign.json @@ -0,0 +1,13 @@ +{ + "action": { + "id": 2, + "status": "in-progress", + "type": "assign_ip", + "started_at": "2014-08-05T15:15:28Z", + "completed_at": null, + "resource_id": 0, + "resource_type": "reserved_ipv6", + "region_slug": "nyc1", + "region": null + } +} \ No newline at end of file diff --git a/spec/fixtures/reserved_ipv6_actions/unassign.json b/spec/fixtures/reserved_ipv6_actions/unassign.json new file mode 100644 index 00000000..6ab8ddac --- /dev/null +++ b/spec/fixtures/reserved_ipv6_actions/unassign.json @@ -0,0 +1,13 @@ +{ + "action": { + "id": 2, + "status": "in-progress", + "type": "unassign_ip", + "started_at": "2014-08-05T15:15:28Z", + "completed_at": null, + "resource_id": 0, + "resource_type": "reserved_ipv6", + "region_slug": "nyc1", + "region": null + } +} \ No newline at end of file diff --git a/spec/lib/droplet_kit/resources/reserved_ipv6_action_resource_spec.rb b/spec/lib/droplet_kit/resources/reserved_ipv6_action_resource_spec.rb new file mode 100644 index 00000000..9e05d494 --- /dev/null +++ b/spec/lib/droplet_kit/resources/reserved_ipv6_action_resource_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DropletKit::ReservedIpv6ActionResource do + subject(:resource) { described_class.new(connection: connection) } + + include_context 'resources' + + describe '#assign' do + let(:ip) { 'fe80::1' } + let(:droplet_id) { 123 } + let(:path) { "/v2/reserved_ipv6/#{ip}/actions" } + let(:action) { 'assign' } + let(:fixture) { api_fixture("reserved_ipv6_actions/#{action}") } + + it 'sends an assign request for a reserved ipv6' do + request = stub_do_api(path).with( + body: { type: action, droplet_id: droplet_id }.to_json + ).to_return(body: fixture, status: 201) + + action = resource.assign(ip: ip, droplet_id: droplet_id) + + expect(request).to have_been_made + + expect(action).to be_a(DropletKit::Action) + expect(action.id).to eq(2) + expect(action.status).to eq('in-progress') + expect(action.type).to eq('assign_ip') + expect(action.started_at).to eq('2014-08-05T15:15:28Z') + expect(action.completed_at).to be_nil + expect(action.resource_id).to eq(0) + expect(action.resource_type).to eq('reserved_ipv6') + expect(action.region_slug).to eq('nyc1') + + expect(action.region).to be_nil + end + + it_behaves_like 'an action that handles invalid parameters' do + let(:arguments) { { ip: ip } } + end + + it_behaves_like 'an action that handles bad request' do + let(:arguments) { { ip: ip, droplet_id: droplet_id } } + end + end + + describe '#unassign' do + let(:ip) { 'fe80::1' } + let(:path) { "/v2/reserved_ipv6/#{ip}/actions" } + let(:action) { 'unassign' } + let(:fixture) { api_fixture("reserved_ipv6_actions/#{action}") } + + it 'sends an unassign request for a reserved ipv6' do + request = stub_do_api(path).with( + body: { type: action }.to_json + ).to_return(body: fixture, status: 201) + + action = resource.unassign(ip: ip) + + expect(request).to have_been_made + + expect(action).to be_a(DropletKit::Action) + expect(action.id).to eq(2) + expect(action.status).to eq('in-progress') + expect(action.type).to eq('unassign_ip') + expect(action.started_at).to eq('2014-08-05T15:15:28Z') + expect(action.completed_at).to be_nil + expect(action.resource_id).to eq(0) + expect(action.resource_type).to eq('reserved_ipv6') + expect(action.region_slug).to eq('nyc1') + + expect(action.region).to be_nil + end + + it_behaves_like 'an action that handles invalid parameters' do + let(:arguments) { { ip: ip} } + end + + it_behaves_like 'an action that handles bad request' do + let(:arguments) { { ip: ip } } + end + end +end diff --git a/spec/lib/droplet_kit/resources/reserved_ipv6_resource_spec.rb b/spec/lib/droplet_kit/resources/reserved_ipv6_resource_spec.rb new file mode 100644 index 00000000..fb366e2b --- /dev/null +++ b/spec/lib/droplet_kit/resources/reserved_ipv6_resource_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'spec_helper' +RSpec.describe DropletKit::ReservedIpv6Resource do + subject(:resource) { described_class.new(connection: connection) } + + include_context 'resources' + + RSpec::Matchers.define :match_reserved_ip_fixture do |droplet| + match do |reserved_ip| + expect(reserved_ip.droplet).to be_a(DropletKit::Droplet) if droplet + + reserved_ip.ip == '2a03:b0c0:3:f0::5dcf:9000' + end + end + + describe '#all' do + it 'returns all of the reserved_ipv6s' do + stub_do_api('/v2/reserved_ipv6', :get).to_return(body: api_fixture('reserved_ipv6/all')) + reserved_ips = resource.all + expect(reserved_ips).to all(be_a(DropletKit::ReservedIpv6)) + + expect(reserved_ips.first).to match_reserved_ip_fixture + end + + it 'returns an empty array of reserved_ipv6s' do + stub_do_api('/v2/reserved_ipv6', :get).to_return(body: api_fixture('reserved_ipv6/all_empty')) + reserved_ips = resource.all.map(&:ip) + expect(reserved_ips).to be_empty + end + + it_behaves_like 'a paginated index' do + let(:fixture_path) { 'reserved_ipv6/all' } + let(:api_path) { '/v2/reserved_ipv6' } + end + end + + describe '#find' do + it 'returns a singular reserved_ipv6' do + stub_do_api('/v2/reserved_ipv6/2a03:b0c0:3:f0::5dcf:9000', :get).to_return(body: api_fixture('reserved_ipv6/find')) + reserved_ip = resource.find(ip: '2a03:b0c0:3:f0::5dcf:9000') + expect(reserved_ip).to be_a(DropletKit::ReservedIpv6) + expect(reserved_ip).to match_reserved_ip_fixture + end + + it 'return a not_found response' do + stub_do_api('/v2/reserved_ipv6/2a03:b0c0:3:f0::5dcf:9000', :get).to_return(body: '{"id": "not_found", "message": "not found"}', status: 404) + expect { resource.find(ip: '2a03:b0c0:3:f0::5dcf:9000') }.to raise_exception(DropletKit::Error).with_message("not found") + end + end + + describe '#create' do + let(:path) { '/v2/reserved_ipv6' } + + context 'for a successful create' do + it 'returns the created reserved_ipv6' do + reserved_ip = DropletKit::ReservedIpv6.new( + region: 'nyc1' + ) + + as_string = DropletKit::ReservedIpv6Mapping.representation_for(:create, reserved_ip) + stub_do_api(path, :post).with(body: as_string).to_return(body: api_fixture('reserved_ipv6/create'), status: 201) + reserved_ip = resource.create(reserved_ip) + expect(reserved_ip.ip).to eq('2604:a880:800:14::79d4:4000') + expect(reserved_ip.region_slug).to eq('nyc1') + end + end + + it_behaves_like 'an action that handles invalid parameters' do + let(:action) { 'create' } + let(:arguments) { DropletKit::ReservedIpv6.new } + end + end + + describe '#delete' do + it 'sends a delete request for the reserved_ipv6' do + request = stub_do_api('/v2/reserved_ipv6/2a03:b0c0:3:f0::5dcf:9000', :delete) + resource.delete(ip: '2a03:b0c0:3:f0::5dcf:9000') + + expect(request).to have_been_made + end + + + it 'return a not_found response' do + stub_do_api('/v2/reserved_ipv6/2a03:b0c0:3:f0::5dcf:9000', :delete).to_return(body: '{"id": "not_found", "message": "not found"}', status: 404) + expect { resource.delete(ip: '2a03:b0c0:3:f0::5dcf:9000') }.to raise_exception(DropletKit::FailedDelete).with_message("not found") + end + end +end diff --git a/spec/support/shared_examples/bad_request_error.rb b/spec/support/shared_examples/bad_request_error.rb new file mode 100644 index 00000000..8ce66a7f --- /dev/null +++ b/spec/support/shared_examples/bad_request_error.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +shared_examples_for 'an action that handles bad request' do + let(:verb) { :post } + let(:exception) { DropletKit::FailedCreate } + + it 'raises an exception with the message attached' do + response_body = { id: :unprocessable_entity, message: 'Something is not right' } + stub_do_api(path, verb).to_return(body: response_body.to_json, status: 400) + + expect { resource.send(action, arguments) }.to raise_exception(exception).with_message(response_body[:message]) + end +end