kozo 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +7 -2
- data/.kzignore +1 -0
- data/.overcommit.yml +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +44 -5
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +156 -48
- data/LICENSE.md +1 -1
- data/README.md +28 -0
- data/Rakefile +3 -7
- data/bin/kozo +6 -2
- data/config/application.rb +3 -0
- data/config/dependencies.rb +4 -25
- data/kozo.gemspec +20 -5
- data/lib/core_ext/boolean.rb +12 -0
- data/lib/core_ext/nil_class.rb +11 -0
- data/lib/core_ext/string.rb +25 -0
- data/lib/kozo/backend.rb +81 -0
- data/lib/kozo/backends/git.rb +50 -0
- data/lib/kozo/backends/local.rb +41 -15
- data/lib/kozo/backends/memory.rb +26 -0
- data/lib/kozo/cli.rb +30 -9
- data/lib/kozo/command.rb +32 -0
- data/lib/kozo/commands/apply.rb +44 -0
- data/lib/kozo/commands/console.rb +15 -0
- data/lib/kozo/commands/import.rb +47 -0
- data/lib/kozo/commands/init.rb +15 -0
- data/lib/kozo/commands/plan.rb +29 -3
- data/lib/kozo/commands/refresh.rb +21 -0
- data/lib/kozo/commands/show.rb +15 -0
- data/lib/kozo/commands/state.rb +64 -0
- data/lib/kozo/commands/validate.rb +13 -0
- data/lib/kozo/commands/version.rb +13 -0
- data/lib/kozo/concerns/assignment.rb +17 -0
- data/lib/kozo/concerns/attributes.rb +99 -0
- data/lib/kozo/concerns/mark.rb +25 -0
- data/lib/kozo/concerns/track.rb +47 -0
- data/lib/kozo/configuration.rb +34 -7
- data/lib/kozo/dsl.rb +18 -12
- data/lib/kozo/error.rb +13 -0
- data/lib/kozo/logger.rb +8 -4
- data/lib/kozo/operation.rb +42 -0
- data/lib/kozo/operations/create.rb +29 -0
- data/lib/kozo/operations/destroy.rb +29 -0
- data/lib/kozo/operations/show.rb +12 -0
- data/lib/kozo/operations/update.rb +19 -0
- data/lib/kozo/options.rb +9 -1
- data/lib/kozo/parser.rb +35 -0
- data/lib/kozo/provider.rb +7 -1
- data/lib/kozo/providers/dummy/dependencies.rb +9 -0
- data/lib/kozo/providers/{null → dummy}/provider.rb +2 -6
- data/lib/kozo/providers/dummy/resource.rb +19 -0
- data/lib/kozo/providers/dummy/resources/dummy.rb +16 -0
- data/lib/kozo/providers/hcloud/dependencies.rb +13 -0
- data/lib/kozo/providers/hcloud/provider.rb +7 -1
- data/lib/kozo/providers/hcloud/resource.rb +46 -1
- data/lib/kozo/providers/hcloud/resources/server.rb +29 -0
- data/lib/kozo/providers/hcloud/resources/ssh_key.rb +6 -2
- data/lib/kozo/resource.rb +110 -3
- data/lib/kozo/state.rb +25 -0
- data/lib/kozo/type.rb +15 -0
- data/lib/kozo/types/boolean.rb +30 -0
- data/lib/kozo/types/date.rb +20 -0
- data/lib/kozo/types/float.rb +17 -0
- data/lib/kozo/types/hash.rb +20 -0
- data/lib/kozo/types/integer.rb +17 -0
- data/lib/kozo/types/string.rb +17 -0
- data/lib/kozo/types/time.rb +18 -0
- data/lib/kozo/version.rb +1 -1
- data/lib/kozo.rb +13 -7
- metadata +195 -31
- data/.github/dependabot.yml +0 -14
- data/.github/workflows/ci.yml +0 -81
- data/bin/bundle +0 -118
- data/bin/console +0 -7
- data/bin/rspec +0 -28
- data/bin/version +0 -62
- data/lib/kozo/backends/base.rb +0 -31
- data/lib/kozo/commands/base.rb +0 -21
- data/lib/kozo/container.rb +0 -35
- data/lib/kozo/environment.rb +0 -27
- data/lib/kozo/providers/null/resource.rb +0 -11
- data/lib/kozo/providers/null/resources/null.rb +0 -13
- data/log/.keep +0 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Attributes
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :attribute_types, default: {}
|
9
|
+
|
10
|
+
def read_attribute(name)
|
11
|
+
value = instance_variable_get(:"@#{name}")
|
12
|
+
|
13
|
+
return value unless value.nil?
|
14
|
+
|
15
|
+
# Set default
|
16
|
+
instance_variable_set(:"@#{name}", (attribute_types[name][:default].dup || (attribute_types[name][:multiple] ? [] : nil)))
|
17
|
+
end
|
18
|
+
|
19
|
+
def write_attribute(name, value)
|
20
|
+
try(:track_change!, name, value)
|
21
|
+
|
22
|
+
value = if attribute_types[name][:multiple]
|
23
|
+
Array(value).map { |v| attribute_types[name][:type].cast(v) }
|
24
|
+
else
|
25
|
+
attribute_types[name][:type].cast(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
instance_variable_set(:"@#{name}", value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# rubocop:disable Metrics/BlockLength
|
33
|
+
class_methods do
|
34
|
+
def inherited(sub_class)
|
35
|
+
super
|
36
|
+
|
37
|
+
sub_class.attribute_types = attribute_types.clone
|
38
|
+
end
|
39
|
+
|
40
|
+
def attribute(name, **options)
|
41
|
+
name = name.to_sym
|
42
|
+
type = Type.lookup(options.fetch(:type, :string))
|
43
|
+
|
44
|
+
try(:track, name)
|
45
|
+
|
46
|
+
options = attribute_types[name] = {
|
47
|
+
multiple: !!options[:multiple],
|
48
|
+
attribute: !!options.fetch(:attribute, true),
|
49
|
+
argument: !!options.fetch(:argument, true),
|
50
|
+
type: type,
|
51
|
+
default: options[:default],
|
52
|
+
}
|
53
|
+
|
54
|
+
# Define getter
|
55
|
+
unless method_defined? name
|
56
|
+
define_method(name) { read_attribute(name) }
|
57
|
+
define_method(:"#{name}?") { !!read_attribute(name) }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Set visibility to public if it's an attribute
|
61
|
+
options[:attribute] ? public(name) : private(name)
|
62
|
+
|
63
|
+
# Define setter
|
64
|
+
define_method(:"#{name}=") { |value| write_attribute(name, value) } unless method_defined? :"#{name}="
|
65
|
+
|
66
|
+
# Set visibility to public if it's an argument
|
67
|
+
options[:argument] ? public(:"#{name}=") : private(:"#{name}=")
|
68
|
+
end
|
69
|
+
|
70
|
+
def attribute_names
|
71
|
+
@attribute_names ||= attribute_types
|
72
|
+
.select { |_k, v| v[:attribute] }
|
73
|
+
.keys
|
74
|
+
end
|
75
|
+
|
76
|
+
def argument_names
|
77
|
+
@argument_names ||= attribute_types
|
78
|
+
.select { |_k, v| v[:argument] }
|
79
|
+
.keys
|
80
|
+
end
|
81
|
+
end
|
82
|
+
# rubocop:enable Metrics/BlockLength
|
83
|
+
|
84
|
+
def attributes
|
85
|
+
attribute_names
|
86
|
+
.map { |name| [name, read_attribute(name)] }
|
87
|
+
.to_h
|
88
|
+
end
|
89
|
+
|
90
|
+
def arguments
|
91
|
+
argument_names
|
92
|
+
.map { |name| [name, read_attribute(name)] }
|
93
|
+
.to_h
|
94
|
+
end
|
95
|
+
|
96
|
+
delegate :attribute_names, to: :class
|
97
|
+
delegate :argument_names, to: :class
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Mark
|
5
|
+
def mark_for_deletion!
|
6
|
+
@status = :delete
|
7
|
+
end
|
8
|
+
|
9
|
+
def marked_for_deletion?
|
10
|
+
@status == :delete
|
11
|
+
end
|
12
|
+
|
13
|
+
def mark_for_creation!
|
14
|
+
@status = :create
|
15
|
+
end
|
16
|
+
|
17
|
+
def marked_for_creation?
|
18
|
+
@status == :create
|
19
|
+
end
|
20
|
+
|
21
|
+
def unmark!
|
22
|
+
@status = nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Track
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :trackables, default: Set.new
|
9
|
+
|
10
|
+
attr_reader :changes
|
11
|
+
|
12
|
+
def changed?
|
13
|
+
changes.any?
|
14
|
+
end
|
15
|
+
|
16
|
+
def clear_changes
|
17
|
+
@changes = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def restore_changes
|
21
|
+
changes.each { |key, (from, _to)| send(:"#{key}=", from) }
|
22
|
+
|
23
|
+
clear_changes
|
24
|
+
end
|
25
|
+
|
26
|
+
def track_change!(name, value)
|
27
|
+
changes[name] = [send(name), value] unless send(name) == value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class_methods do
|
32
|
+
def track(name)
|
33
|
+
trackables << name
|
34
|
+
|
35
|
+
define_method(:"#{name}_change") { changes[name] } unless method_defined? :"#{name}_change"
|
36
|
+
define_method(:"#{name}_changed?") { !!changes[name] } unless method_defined? :"#{name}_changed?"
|
37
|
+
define_method(:"#{name}_was") { changes.dig(name, 0) } unless method_defined? :"#{name}_was"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(...)
|
42
|
+
@changes = {}
|
43
|
+
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/kozo/configuration.rb
CHANGED
@@ -8,18 +8,45 @@ module Kozo
|
|
8
8
|
def initialize(directory)
|
9
9
|
@directory = directory
|
10
10
|
@providers = {}
|
11
|
-
@resources =
|
11
|
+
@resources = Set.new
|
12
12
|
end
|
13
13
|
|
14
14
|
def backend
|
15
|
-
@backend ||=
|
15
|
+
@backend ||= Kozo
|
16
|
+
.container
|
17
|
+
.resolve("backend.local", self, directory)
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def changes
|
21
|
+
@changes ||= begin
|
22
|
+
# Copy resources in state
|
23
|
+
changes = backend
|
24
|
+
.state
|
25
|
+
.resources
|
26
|
+
.map(&:dup)
|
27
|
+
.each(&:clear_changes)
|
28
|
+
.each do |resource|
|
29
|
+
configured = resources.find { |r| r.address == resource.address }
|
30
|
+
|
31
|
+
# Assign updated attributes (mark for update)
|
32
|
+
resource.assign_attributes(configured&.attributes&.except(:id) || resource.attributes.except(:id).transform_values { nil })
|
33
|
+
|
34
|
+
# Mark for deletion
|
35
|
+
resource.mark_for_deletion! unless configured
|
36
|
+
end
|
37
|
+
|
38
|
+
# Append resources not in state
|
39
|
+
changes += resources
|
40
|
+
.reject { |r| backend.state.resources.any? { |res| res.address == r.address } }
|
41
|
+
.map { |r| r.class.new(state_name: r.state_name, **r.arguments) }
|
42
|
+
.each(&:mark_for_creation!)
|
43
|
+
|
44
|
+
changes
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
"directory: #{directory}"
|
23
50
|
end
|
24
51
|
end
|
25
52
|
end
|
data/lib/kozo/dsl.rb
CHANGED
@@ -13,7 +13,7 @@ module Kozo
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def backend(type)
|
16
|
-
backend = resolve(:backend, type)
|
16
|
+
backend = resolve(:backend, type, configuration)
|
17
17
|
|
18
18
|
yield backend if block_given?
|
19
19
|
|
@@ -25,28 +25,34 @@ module Kozo
|
|
25
25
|
|
26
26
|
yield provider if block_given?
|
27
27
|
|
28
|
-
|
28
|
+
provider.setup
|
29
|
+
|
30
|
+
configuration.providers[provider.provider_name] = provider
|
29
31
|
end
|
30
32
|
|
31
|
-
def resource(type,
|
32
|
-
resource = resolve(:resource, type
|
33
|
-
resource.
|
33
|
+
def resource(type, state_name)
|
34
|
+
resource = resolve(:resource, type)
|
35
|
+
resource.state_name = state_name
|
36
|
+
|
37
|
+
raise InvalidResource, "resource #{resource.address} already defined" if configuration.resources.include?(resource)
|
38
|
+
|
39
|
+
resource.provider = configuration.providers[resource.provider_name]
|
34
40
|
|
35
|
-
|
41
|
+
raise InvalidResource, "provider #{resource.provider_name} not configured" unless resource.provider
|
36
42
|
|
37
43
|
yield resource if block_given?
|
38
44
|
|
39
|
-
configuration.resources
|
45
|
+
configuration.resources << resource
|
40
46
|
end
|
41
47
|
|
42
48
|
private
|
43
49
|
|
44
|
-
def resolve(resource, type,
|
45
|
-
Kozo.logger.debug "Initializing #{resource} #{type} #{
|
50
|
+
def resolve(resource, type, *args)
|
51
|
+
Kozo.logger.debug "Initializing #{resource} #{type}#{" with options #{args.join(' ')}" if args.any?}"
|
46
52
|
|
47
|
-
Kozo.container.resolve("#{resource}.#{type}")
|
48
|
-
rescue Container::DependencyNotRegistered
|
49
|
-
|
53
|
+
Kozo.container.resolve("#{resource}.#{type}", *args)
|
54
|
+
rescue Dinja::Container::DependencyNotRegistered
|
55
|
+
raise InvalidResource, "unknown #{resource} type: #{type}"
|
50
56
|
end
|
51
57
|
end
|
52
58
|
end
|
data/lib/kozo/error.rb
ADDED
data/lib/kozo/logger.rb
CHANGED
@@ -12,18 +12,22 @@ module Kozo
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def level
|
15
|
-
ENV.fetch("LOG_LEVEL", "info")
|
15
|
+
Kozo.options.verbose? ? "debug" : ENV.fetch("LOG_LEVEL", "info")
|
16
16
|
end
|
17
17
|
|
18
18
|
def formatter
|
19
|
-
|
19
|
+
Formatter.new
|
20
20
|
end
|
21
21
|
|
22
22
|
class Formatter < ::Logger::Formatter
|
23
23
|
def call(severity, _time, _progname, msg)
|
24
|
-
abort("#{File.basename($PROGRAM_NAME)}: #{msg[0].downcase}#{msg[1..]}") if severity == "FATAL"
|
24
|
+
abort("#{File.basename($PROGRAM_NAME)}: #{msg[0].downcase}#{msg[1..]}".white.on_red) if severity == "FATAL"
|
25
25
|
|
26
|
-
"#{msg}\n"
|
26
|
+
msg = "#{msg}\n"
|
27
|
+
msg = msg.yellow if severity == "DEBUG"
|
28
|
+
msg = msg.red if severity == "ERROR"
|
29
|
+
|
30
|
+
msg
|
27
31
|
end
|
28
32
|
end
|
29
33
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
class Operation
|
5
|
+
attr_reader :resource
|
6
|
+
|
7
|
+
class_attribute :symbol, :display_symbol
|
8
|
+
|
9
|
+
def initialize(resource)
|
10
|
+
@resource = resource
|
11
|
+
end
|
12
|
+
|
13
|
+
def apply(_state)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
resource_to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def resource_to_s
|
24
|
+
<<~DSL.chomp
|
25
|
+
#{"# #{resource.address}:".bold}
|
26
|
+
#{display_symbol} resource "#{resource.resource_name}", "#{resource.state_name}" do |r|
|
27
|
+
#{attributes_to_s}
|
28
|
+
end
|
29
|
+
|
30
|
+
DSL
|
31
|
+
end
|
32
|
+
|
33
|
+
def attributes_to_s
|
34
|
+
l = resource.attribute_names.map(&:length).max || 1
|
35
|
+
|
36
|
+
resource
|
37
|
+
.attributes
|
38
|
+
.map { |k, v| "#{resource.changes.key?(k) ? display_symbol : ' '} r.#{k.to_s.ljust(l)} = \"#{v.to_s.chomp.truncate(75)}\"" }
|
39
|
+
.join("\n ")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Operations
|
5
|
+
class Create < Operation
|
6
|
+
self.symbol = :+
|
7
|
+
self.display_symbol = "+".green
|
8
|
+
|
9
|
+
def apply(state)
|
10
|
+
# Create resource in remote infrastructure
|
11
|
+
resource.create!
|
12
|
+
|
13
|
+
# Add resource to local state
|
14
|
+
state.resources << resource
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def attributes_to_s
|
20
|
+
l = resource.attribute_names.map(&:length).max || 1
|
21
|
+
|
22
|
+
resource
|
23
|
+
.attributes
|
24
|
+
.map { |k, v| "#{resource.changes.key?(k) ? display_symbol : ' '} r.#{k.to_s.ljust(l)} = #{v.blank? ? '(known after apply)' : "\"#{v.to_s.chomp.truncate(75)}\""}" }
|
25
|
+
.join("\n ")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Operations
|
5
|
+
class Destroy < Operation
|
6
|
+
self.symbol = :-
|
7
|
+
self.display_symbol = "-".red
|
8
|
+
|
9
|
+
def apply(state)
|
10
|
+
# Destroy resource in remote infrastructure
|
11
|
+
resource.destroy!
|
12
|
+
|
13
|
+
# Delete resource from local state
|
14
|
+
state.resources.delete(resource)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def attributes_to_s
|
20
|
+
l = resource.attribute_names.map(&:length).max || 1
|
21
|
+
|
22
|
+
resource
|
23
|
+
.attribute_names
|
24
|
+
.map { |k| "#{display_symbol} r.#{k.to_s.ljust(l)} = \"#{resource.send(:"#{k}_was").to_s.chomp.truncate(75)}\"" }
|
25
|
+
.join("\n ")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Operations
|
5
|
+
class Update < Operation
|
6
|
+
self.symbol = :~
|
7
|
+
self.display_symbol = "~".yellow
|
8
|
+
|
9
|
+
def apply(state)
|
10
|
+
# Update resource in remote infrastructure
|
11
|
+
resource.update!
|
12
|
+
|
13
|
+
# Update resource in local state
|
14
|
+
state.resources.delete(resource)
|
15
|
+
state.resources << resource
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/kozo/options.rb
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
module Kozo
|
4
4
|
class Options
|
5
|
-
|
5
|
+
attr_accessor :help
|
6
|
+
|
7
|
+
def directory=(path)
|
8
|
+
@directory = File.expand_path(path)
|
9
|
+
end
|
6
10
|
|
7
11
|
def directory
|
8
12
|
@directory ||= Dir.pwd
|
@@ -12,6 +16,10 @@ module Kozo
|
|
12
16
|
directory.present?
|
13
17
|
end
|
14
18
|
|
19
|
+
def verbose=(value)
|
20
|
+
@verbose = value.present?
|
21
|
+
end
|
22
|
+
|
15
23
|
def verbose
|
16
24
|
@verbose ||= false
|
17
25
|
end
|
data/lib/kozo/parser.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
class Parser
|
5
|
+
attr_reader :directory
|
6
|
+
|
7
|
+
def initialize(directory)
|
8
|
+
@directory = directory
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
configuration = Configuration.new(directory)
|
13
|
+
dsl = DSL.new(configuration)
|
14
|
+
|
15
|
+
Dir[File.join(directory, "main.kz"), File.join(directory, "**", "*.kz")]
|
16
|
+
.uniq
|
17
|
+
.reject { |file| ignores.any? { |ignore| file.include?(ignore) } }
|
18
|
+
.each { |file| dsl.instance_eval(File.read(file)) }
|
19
|
+
|
20
|
+
configuration
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def ignores
|
26
|
+
@ignores ||= begin
|
27
|
+
File
|
28
|
+
.readlines(File.join(directory, ".kzignore"))
|
29
|
+
.map(&:chomp)
|
30
|
+
rescue Errno::ENOENT
|
31
|
+
[]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/kozo/provider.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Providers
|
5
|
+
module Dummy
|
6
|
+
class Resource < Kozo::Resource
|
7
|
+
self.provider_name = "dummy"
|
8
|
+
|
9
|
+
def refresh!; end
|
10
|
+
|
11
|
+
def create!; end
|
12
|
+
|
13
|
+
def destroy!; end
|
14
|
+
|
15
|
+
def update!; end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
register("provider.hcloud") do
|
4
|
+
Kozo::Providers::HCloud::Provider.new
|
5
|
+
end
|
6
|
+
|
7
|
+
register("resource.hcloud_ssh_key") do
|
8
|
+
Kozo::Providers::HCloud::Resources::SSHKey.new
|
9
|
+
end
|
10
|
+
|
11
|
+
register("resource.hcloud_server") do
|
12
|
+
Kozo::Providers::HCloud::Resources::Server.new
|
13
|
+
end
|
@@ -1,12 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "hcloud"
|
4
|
+
|
3
5
|
module Kozo
|
4
6
|
module Providers
|
5
7
|
module HCloud
|
6
8
|
class Provider < Kozo::Provider
|
7
9
|
attr_accessor :key
|
8
10
|
|
9
|
-
self.
|
11
|
+
self.provider_name = "hcloud"
|
12
|
+
|
13
|
+
def setup
|
14
|
+
::HCloud::Client.connection = ::HCloud::Client.new(access_token: key)
|
15
|
+
end
|
10
16
|
|
11
17
|
def ==(other)
|
12
18
|
key == other.key
|
@@ -4,7 +4,52 @@ module Kozo
|
|
4
4
|
module Providers
|
5
5
|
module HCloud
|
6
6
|
class Resource < Kozo::Resource
|
7
|
-
self.
|
7
|
+
self.provider_name = "hcloud"
|
8
|
+
|
9
|
+
attribute :id, type: :integer
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def refresh
|
14
|
+
resource = resource_class.find(id)
|
15
|
+
|
16
|
+
attribute_names
|
17
|
+
.excluding(:id)
|
18
|
+
.each { |attr| send(:"#{attr}=", resource.send(attr)) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
resource = resource_class.new(**attributes.except(:id))
|
23
|
+
resource.create
|
24
|
+
|
25
|
+
attribute_names
|
26
|
+
.each { |attr| send(:"#{attr}=", resource.send(attr)) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def update
|
30
|
+
resource = resource_class.find(id)
|
31
|
+
|
32
|
+
attribute_names
|
33
|
+
.excluding(:id)
|
34
|
+
.each { |attr| resource.send(:"#{attr}=", send(attr)) }
|
35
|
+
|
36
|
+
resource.update
|
37
|
+
|
38
|
+
attribute_names
|
39
|
+
.excluding(:id)
|
40
|
+
.each { |attr| send(:"#{attr}=", resource.send(attr)) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def destroy
|
44
|
+
resource = resource_class.find(id)
|
45
|
+
resource.delete
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def resource_class
|
51
|
+
"HCloud::#{self.class.name.demodulize}".constantize
|
52
|
+
end
|
8
53
|
end
|
9
54
|
end
|
10
55
|
end
|