Skip to content

Commit

Permalink
Merge pull request #60 from aufziehvogel/30-complete-resources
Browse files Browse the repository at this point in the history
add missing features to existing resources
  • Loading branch information
Stefan Koch committed Oct 11, 2023
2 parents 5a5857e + 8b22d07 commit 04d3013
Show file tree
Hide file tree
Showing 32 changed files with 641 additions and 433 deletions.
1 change: 1 addition & 0 deletions lib/hcloud.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module Hcloud
autoload :TyphoeusExt, 'hcloud/typhoeus_ext'
autoload :AbstractResource, 'hcloud/abstract_resource'
autoload :EntryLoader, 'hcloud/entry_loader'
autoload :ResourceLoader, 'hcloud/resource_loader'

autoload :Server, 'hcloud/server'
autoload :ServerResource, 'hcloud/server_resource'
Expand Down
38 changes: 3 additions & 35 deletions lib/hcloud/entry_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -223,49 +223,17 @@ def _update_attribute(key, value)
instance_variable_set("@#{key}", value)
end

# rubocop: disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
def _load(resource)
return if resource.nil?

@_attributes = {}.with_indifferent_access

resource.each do |key, value|
definition = self.class.schema[key]

if definition == :time
_update_attribute(key, value ? Time.parse(value) : nil)
next
end

if definition.is_a?(Class) && definition.include?(EntryLoader)
_update_attribute(key, value ? definition.new(client, value) : nil)
next
end

# if schema definition is [Class]
if definition.is_a?(Array) && definition.first.include?(EntryLoader)

# just set attribute to an empty array if value is no array or empty
if !value.is_a?(Array) || value.empty?
_update_attribute(key, [])
next
end

if value.first.is_a?(Integer)
# If value is an integer, this is the id of an object which's class can be
# retreived from definition. Load a future object that can on access retreive the
# data from the api and convert it to a proper object.
_update_attribute(key, value.map { |id| Future.new(client, definition.first, id) })
else
# Otherwise the value *is* the content of the object
_update_attribute(key, value.map { |item| definition.first.new(client, item) })
end
next
end
loader = Hcloud::ResourceLoader.new(self.class.schema, client: client)
loaded_data = loader.load(resource)

loaded_data.each do |key, value|
_update_attribute(key, value.is_a?(Hash) ? value.with_indifferent_access : value)
end
end
# rubocop: enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
end
end
2 changes: 1 addition & 1 deletion lib/hcloud/floating_ip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class FloatingIP
)

protectable :delete
updatable :description
updatable :name, :description
destructible

has_actions
Expand Down
2 changes: 1 addition & 1 deletion lib/hcloud/floating_ip_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def [](arg)
end
end

def create(type:, server: nil, home_location: nil, description: nil, labels: {})
def create(type:, name: nil, server: nil, home_location: nil, description: nil, labels: {})
raise Hcloud::Error::InvalidInput, 'no type given' if type.blank?
if server.nil? && home_location.nil?
raise Hcloud::Error::InvalidInput, 'either server or home_location must be given'
Expand Down
5 changes: 4 additions & 1 deletion lib/hcloud/future.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

module Hcloud
class Future < Delegator
attr_reader :raw_data

# rubocop: disable Lint/MissingSuper
def initialize(client, target_class, id)
def initialize(client, target_class, id, raw_data: nil)
@target_class = target_class
@id = id
@raw_data = raw_data
@__client = client
end
# rubocop: enable Lint/MissingSuper
Expand Down
2 changes: 2 additions & 0 deletions lib/hcloud/image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class Image
updatable :description, :type
destructible

has_actions

def to_snapshot
update(type: 'snapshot')
end
Expand Down
2 changes: 1 addition & 1 deletion lib/hcloud/image_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Hcloud
class ImageResource < AbstractResource
filter_attributes :type, :bound_to, :name, :label_selector
filter_attributes :type, :bound_to, :name, :label_selector, :status, :include_deprecated

bind_to Image

Expand Down
6 changes: 6 additions & 0 deletions lib/hcloud/network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,11 @@ def del_route(destination:, gateway:)

prepare_request('actions/delete_route', j: COLLECT_ARGS.call(__method__, binding))
end

def change_ip_range(ip_range:)
raise Hcloud::Error::InvalidInput, 'no ip_range given' if ip_range.blank?

prepare_request('actions/change_ip_range', j: COLLECT_ARGS.call(__method__, binding))
end
end
end
2 changes: 1 addition & 1 deletion lib/hcloud/placement_group_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Hcloud
class PlacementGroupResource < AbstractResource
filter_attributes :type, :name
filter_attributes :type, :name, :label_selector

bind_to PlacementGroup

Expand Down
62 changes: 62 additions & 0 deletions lib/hcloud/resource_loader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

require 'time'

module Hcloud
class ResourceLoader
def initialize(schema, client:)
@schema = schema
@client = client
end

def load(data)
load_with_schema(@schema, data)
end

private

def load_with_schema(schema, data)
if schema.respond_to?(:call)
schema.call(data, @client)
elsif schema.is_a?(Array) && schema.count.positive? && data.is_a?(Array)
load_array(schema, data)
elsif schema.is_a?(Hash) && data.is_a?(Hash)
load_hash(schema, data)
else
load_single_item(schema, data)
end
end

def load_array(schema, data)
data.map do |item|
load_with_schema(schema[0], item)
end
end

def load_hash(schema, data)
data.map do |key, value|
[key, load_with_schema(schema[key], value)]
end.to_h
end

def load_single_item(definition, value)
if definition == :time
return value ? Time.parse(value) : nil
end

if definition.is_a?(Class) && definition.include?(EntryLoader)
return if value.nil?

# If value is an integer, this is the id of an object which's class can be
# retreived from definition. Load a future object that can on access retreive the
# data from the api and convert it to a proper object.
return Future.new(@client, definition, value) if value.is_a?(Integer)

# Otherwise the value *is* the content of the object
return definition.new(@client, value)
end

value
end
end
end
40 changes: 39 additions & 1 deletion lib/hcloud/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,24 @@ class Server
datacenter: Datacenter,
image: Image,
iso: Iso,
load_balancers: [LoadBalancer],
placement_group: PlacementGroup,
private_net: [Network],
public_net: {
ipv4: lambda do |data, client|
Future.new(client, PrimaryIP, data[:id]) if data.to_h[:id].is_a?(Integer)
end,
ipv6: lambda do |data, client|
Future.new(client, PrimaryIP, data[:id]) if data.to_h[:id].is_a?(Integer)
end,
floating_ips: [FloatingIP],
firewalls: [
lambda do |data, client|
Future.new(client, Firewall, data[:id], raw_data: data) if data.to_h[:id].is_a?(Integer)
end
]
# firewalls: [{ id: Firewall }]
},
volumes: [Volume]
)

Expand All @@ -21,6 +38,7 @@ class Server
destructible

has_actions
has_metrics

def enable_rescue(type: 'linux64', ssh_keys: [])
query = COLLECT_ARGS.call(__method__, binding)
Expand Down Expand Up @@ -73,10 +91,30 @@ def detach_from_network(network:)
prepare_request('actions/detach_from_network', j: { network: network })
end

def add_to_placement_group(placement_group:)
raise Hcloud::Error::InvalidInput, 'no placement_group given' if placement_group.nil?

prepare_request('actions/add_to_placement_group', j: COLLECT_ARGS.call(__method__, binding))
end

def change_alias_ips(alias_ips:, network:)
raise Hcloud::Error::InvalidInput, 'no alias_ips given' if alias_ips.to_a.count.zero?
raise Hcloud::Error::InvalidInput, 'no network given' if network.nil?

prepare_request('actions/change_alias_ips', j: COLLECT_ARGS.call(__method__, binding))
end

def change_dns_ptr(ip:, dns_ptr:)
raise Hcloud::Error::InvalidInput, 'no IP given' if ip.blank?
raise Hcloud::Error::InvalidInput, 'no dns_ptr given' if dns_ptr.blank?

prepare_request('actions/change_dns_ptr', j: COLLECT_ARGS.call(__method__, binding))
end

%w[
poweron poweroff shutdown reboot reset
disable_rescue disable_backup detach_iso
request_console
request_console remove_from_placement_group
].each do |action|
define_method(action) do
prepare_request("actions/#{action}", method: :post)
Expand Down
5 changes: 5 additions & 0 deletions lib/hcloud/server_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ def create(name:,
location: nil,
start_after_create: nil,
ssh_keys: [],
public_net: nil,
firewalls: nil,
networks: [],
placement_group: nil,
user_data: nil,
volumes: nil,
automount: nil,
labels: {})
prepare_request('servers', j: COLLECT_ARGS.call(__method__, binding),
expected_code: 201) do |response|
Expand Down
2 changes: 1 addition & 1 deletion lib/hcloud/ssh_key_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Hcloud
class SSHKeyResource < AbstractResource
filter_attributes :name, :label_selector
filter_attributes :name, :label_selector, :fingerprint

def [](arg)
case arg
Expand Down
2 changes: 1 addition & 1 deletion lib/hcloud/volume_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Hcloud
class VolumeResource < AbstractResource
filter_attributes :name, :label_selector
filter_attributes :name, :label_selector, :status

def [](arg)
case arg
Expand Down
47 changes: 30 additions & 17 deletions spec/doubles/servers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,7 @@ def new_server(**kwargs)
:running, :initializing, :starting, :stopping, :off, :deleting, :migrating, :rebuilding, :unknown
),
created: Faker::Time.backward,
public_net: {
ipv4: {
ip: Faker::Internet.ip_v4_address,
blocked: random_choice(true, false),
dns_ptr: Faker::Internet.domain_name
},
ipv6: {
ip: Faker::Internet.ip_v6_cidr,
blocked: random_choice(true, false),
dns_ptr: random_choice(
[],
[{ ip: Faker::Internet.ip_v6_address, dns_ptr: Faker::Internet.domain_name }]
)
},
floating_ips: []
},
public_net: new_server_public_net,
private_net: [{
alias_ips: [],
ip: '10.0.0.2',
Expand All @@ -143,8 +128,36 @@ def new_server(**kwargs)
ingoing_traffic: Faker::Number.number,
included_traffic: Faker::Number.number,
protection: { delete: random_choice(true, false), rebuild: random_choice(true, false) },
placement_group: random_choice(nil, new_placement_group),
load_balancers: Array.new(Faker::Number.within(range: 0..3)).map { Faker::Number.number },
labels: {},
volumes: []
volumes: Array.new(Faker::Number.within(range: 0..2)).map { new_volume }
}.deep_merge(kwargs)
end

private

def new_server_public_net
{
ipv4: {
id: Faker::Number.number,
ip: Faker::Internet.ip_v4_address,
blocked: random_choice(true, false),
dns_ptr: Faker::Internet.domain_name
},
ipv6: {
id: Faker::Number.number,
ip: Faker::Internet.ip_v6_cidr,
blocked: random_choice(true, false),
dns_ptr: random_choice(
[],
[{ ip: Faker::Internet.ip_v6_address, dns_ptr: Faker::Internet.domain_name }]
)
},
firewalls: Array.new(Faker::Number.within(range: 0..5)).map do
{ id: Faker::Number.number, status: 'applied' }
end,
floating_ips: Array.new(Faker::Number.within(range: 0..3)).map { Faker::Number.number }
}
end
end
16 changes: 4 additions & 12 deletions spec/hcloud/certificate_actions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
require 'spec_helper'

describe Hcloud::Certificate, doubles: :certificate do
include_context 'test doubles'
include_context 'action tests'

let :certificates do
Array.new(Faker::Number.within(range: 10..50)).map { new_certificate }
end
Expand All @@ -21,18 +24,7 @@

context '#retry' do
it 'works' do
stub_action(:certificates, certificate[:id], :retry) do |_req, _info|
{
action: build_action_resp(
:issue_certificate, :running,
resources: [{ id: 42, type: 'certificate' }]
)
}
end

action = certificate_obj.retry
expect(action).to be_a Hcloud::Action
expect(action.command).to eq('issue_certificate')
test_action(:retry, :issue_certificate)
end
end
end
Loading

0 comments on commit 04d3013

Please sign in to comment.