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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -2
  3. data/.kzignore +1 -0
  4. data/.overcommit.yml +1 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +44 -5
  7. data/CHANGELOG.md +5 -0
  8. data/Gemfile +3 -1
  9. data/Gemfile.lock +156 -48
  10. data/LICENSE.md +1 -1
  11. data/README.md +28 -0
  12. data/Rakefile +3 -7
  13. data/bin/kozo +6 -2
  14. data/config/application.rb +3 -0
  15. data/config/dependencies.rb +4 -25
  16. data/kozo.gemspec +20 -5
  17. data/lib/core_ext/boolean.rb +12 -0
  18. data/lib/core_ext/nil_class.rb +11 -0
  19. data/lib/core_ext/string.rb +25 -0
  20. data/lib/kozo/backend.rb +81 -0
  21. data/lib/kozo/backends/git.rb +50 -0
  22. data/lib/kozo/backends/local.rb +41 -15
  23. data/lib/kozo/backends/memory.rb +26 -0
  24. data/lib/kozo/cli.rb +30 -9
  25. data/lib/kozo/command.rb +32 -0
  26. data/lib/kozo/commands/apply.rb +44 -0
  27. data/lib/kozo/commands/console.rb +15 -0
  28. data/lib/kozo/commands/import.rb +47 -0
  29. data/lib/kozo/commands/init.rb +15 -0
  30. data/lib/kozo/commands/plan.rb +29 -3
  31. data/lib/kozo/commands/refresh.rb +21 -0
  32. data/lib/kozo/commands/show.rb +15 -0
  33. data/lib/kozo/commands/state.rb +64 -0
  34. data/lib/kozo/commands/validate.rb +13 -0
  35. data/lib/kozo/commands/version.rb +13 -0
  36. data/lib/kozo/concerns/assignment.rb +17 -0
  37. data/lib/kozo/concerns/attributes.rb +99 -0
  38. data/lib/kozo/concerns/mark.rb +25 -0
  39. data/lib/kozo/concerns/track.rb +47 -0
  40. data/lib/kozo/configuration.rb +34 -7
  41. data/lib/kozo/dsl.rb +18 -12
  42. data/lib/kozo/error.rb +13 -0
  43. data/lib/kozo/logger.rb +8 -4
  44. data/lib/kozo/operation.rb +42 -0
  45. data/lib/kozo/operations/create.rb +29 -0
  46. data/lib/kozo/operations/destroy.rb +29 -0
  47. data/lib/kozo/operations/show.rb +12 -0
  48. data/lib/kozo/operations/update.rb +19 -0
  49. data/lib/kozo/options.rb +9 -1
  50. data/lib/kozo/parser.rb +35 -0
  51. data/lib/kozo/provider.rb +7 -1
  52. data/lib/kozo/providers/dummy/dependencies.rb +9 -0
  53. data/lib/kozo/providers/{null → dummy}/provider.rb +2 -6
  54. data/lib/kozo/providers/dummy/resource.rb +19 -0
  55. data/lib/kozo/providers/dummy/resources/dummy.rb +16 -0
  56. data/lib/kozo/providers/hcloud/dependencies.rb +13 -0
  57. data/lib/kozo/providers/hcloud/provider.rb +7 -1
  58. data/lib/kozo/providers/hcloud/resource.rb +46 -1
  59. data/lib/kozo/providers/hcloud/resources/server.rb +29 -0
  60. data/lib/kozo/providers/hcloud/resources/ssh_key.rb +6 -2
  61. data/lib/kozo/resource.rb +110 -3
  62. data/lib/kozo/state.rb +25 -0
  63. data/lib/kozo/type.rb +15 -0
  64. data/lib/kozo/types/boolean.rb +30 -0
  65. data/lib/kozo/types/date.rb +20 -0
  66. data/lib/kozo/types/float.rb +17 -0
  67. data/lib/kozo/types/hash.rb +20 -0
  68. data/lib/kozo/types/integer.rb +17 -0
  69. data/lib/kozo/types/string.rb +17 -0
  70. data/lib/kozo/types/time.rb +18 -0
  71. data/lib/kozo/version.rb +1 -1
  72. data/lib/kozo.rb +13 -7
  73. metadata +195 -31
  74. data/.github/dependabot.yml +0 -14
  75. data/.github/workflows/ci.yml +0 -81
  76. data/bin/bundle +0 -118
  77. data/bin/console +0 -7
  78. data/bin/rspec +0 -28
  79. data/bin/version +0 -62
  80. data/lib/kozo/backends/base.rb +0 -31
  81. data/lib/kozo/commands/base.rb +0 -21
  82. data/lib/kozo/container.rb +0 -35
  83. data/lib/kozo/environment.rb +0 -27
  84. data/lib/kozo/providers/null/resource.rb +0 -11
  85. data/lib/kozo/providers/null/resources/null.rb +0 -13
  86. data/log/.keep +0 -0
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ module Providers
5
+ module HCloud
6
+ module Resources
7
+ class Server < Resource
8
+ self.resource_name = "hcloud_server"
9
+
10
+ attribute :name
11
+ attribute :image
12
+ attribute :server_type
13
+ attribute :location
14
+ attribute :datacenter
15
+
16
+ attribute :user_data
17
+
18
+ attribute :labels, type: :hash
19
+
20
+ attribute :ssh_keys, multiple: true
21
+
22
+ attribute :locked, argument: false, type: :boolean
23
+
24
+ attribute :created, argument: false, type: :time
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -5,9 +5,13 @@ module Kozo
5
5
  module HCloud
6
6
  module Resources
7
7
  class SSHKey < Resource
8
- attr_accessor :name, :public_key
8
+ self.resource_name = "hcloud_ssh_key"
9
9
 
10
- self.name = "hcloud_ssh_key"
10
+ attribute :name
11
+ attribute :public_key
12
+ attribute :labels, type: :hash
13
+
14
+ attribute :created, type: :time, argument: false
11
15
  end
12
16
  end
13
17
  end
data/lib/kozo/resource.rb CHANGED
@@ -2,12 +2,119 @@
2
2
 
3
3
  module Kozo
4
4
  class Resource
5
- attr_accessor :id, :provider
5
+ include Attributes
6
6
 
7
- class_attribute :name, :provider
7
+ include Assignment
8
+ include Mark
9
+ include Track
10
+
11
+ attr_accessor :provider, :state_name
12
+
13
+ attribute :id
14
+
15
+ class_attribute :resource_name, :provider_name
16
+
17
+ def address
18
+ "#{resource_name}.#{state_name}"
19
+ end
8
20
 
9
21
  def ==(other)
10
- id == other.id
22
+ self.class == other.class &&
23
+ id == other.id &&
24
+ state_name == other.state_name
25
+ end
26
+
27
+ alias eql? ==
28
+
29
+ def hash
30
+ [self.class, id].hash
31
+ end
32
+
33
+ def to_h
34
+ {
35
+ meta: meta,
36
+ data: attributes,
37
+ }
38
+ end
39
+
40
+ def self.from_h(hash)
41
+ Kozo
42
+ .container
43
+ .resolve("resource.#{hash.dig(:meta, :resource)}")
44
+ .tap { |r| hash[:data].each { |k, v| r.send(:"#{k}=", v) } }
45
+ .tap { |r| r.state_name = hash.dig(:meta, :name) }
46
+ end
47
+
48
+ ##
49
+ # Refetch resource from remote infrastructure
50
+ #
51
+ def refresh!
52
+ Kozo.logger.info "#{address}: refreshing state"
53
+
54
+ refresh
55
+
56
+ Kozo.logger.info "#{address}: refreshed state"
57
+ end
58
+
59
+ ##
60
+ # Create resource in remote infrastructure
61
+ #
62
+ def create!
63
+ Kozo.logger.info "#{address}: creating resource"
64
+
65
+ create
66
+
67
+ Kozo.logger.info "#{address}: created resource"
68
+ end
69
+
70
+ ##
71
+ # Update resource in remote infrastructure
72
+ #
73
+ def update!
74
+ Kozo.logger.info "#{address}: updating resource"
75
+
76
+ update
77
+
78
+ Kozo.logger.info "#{address}: updated resource"
79
+ end
80
+
81
+ ##
82
+ # Destroy resource in remote infrastructure
83
+ #
84
+ def destroy!
85
+ Kozo.logger.info "#{address}: destroying resource"
86
+
87
+ destroy
88
+
89
+ Kozo.logger.info "#{address}: destroyed resource"
90
+ end
91
+
92
+ protected
93
+
94
+ def refresh
95
+ raise NotImplementedError
96
+ end
97
+
98
+ def create
99
+ raise NotImplementedError
100
+ end
101
+
102
+ def update
103
+ raise NotImplementedError
104
+ end
105
+
106
+ def destroy
107
+ raise NotImplementedError
108
+ end
109
+
110
+ private
111
+
112
+ def meta
113
+ {
114
+ name: state_name,
115
+ provider: provider_name,
116
+ resource: resource_name,
117
+ }
11
118
  end
12
119
  end
13
120
  end
data/lib/kozo/state.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ class State
5
+ VERSION = 1
6
+
7
+ attr_accessor :resources
8
+
9
+ def initialize(resources = nil)
10
+ @resources = Set.new(resources)
11
+ end
12
+
13
+ def ==(other)
14
+ resources == other.resources
15
+ end
16
+
17
+ def to_h
18
+ {
19
+ version: VERSION,
20
+ kozo_version: Kozo::VERSION,
21
+ resources: resources.map(&:to_h),
22
+ }
23
+ end
24
+ end
25
+ end
data/lib/kozo/type.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ class Type
5
+ def self.lookup(type)
6
+ "Kozo::Types::#{type.to_s.camelize}".constantize
7
+ rescue NameError
8
+ raise ArgumentError, "No such type: #{type}"
9
+ end
10
+
11
+ def self.cast(value); end
12
+
13
+ def self.serialize(value); end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ module Types
5
+ class Boolean < Type
6
+ # rubocop:disable Lint/BooleanSymbol
7
+ FALSE_VALUES = [
8
+ false, 0,
9
+ "0", :"0",
10
+ "f", :f,
11
+ "F", :F,
12
+ "false", :false,
13
+ "FALSE", :FALSE,
14
+ "off", :off,
15
+ "OFF", :OFF,
16
+ ].freeze
17
+ # rubocop:enable Lint/BooleanSymbol
18
+
19
+ def self.cast(value)
20
+ return if value.nil?
21
+
22
+ !FALSE_VALUES.include?(value)
23
+ end
24
+
25
+ def self.serialize(value)
26
+ value
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ module Types
5
+ class Date < Type
6
+ def self.cast(value)
7
+ return unless value
8
+ return value if value.is_a?(::Date)
9
+
10
+ ::Date.parse(value)
11
+ rescue ::Date::Error => e
12
+ raise ArgumentError, e
13
+ end
14
+
15
+ def self.serialize(value)
16
+ value&.iso8601
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ module Types
5
+ class Float < Type
6
+ def self.cast(value)
7
+ return unless value
8
+
9
+ send :Float, value
10
+ end
11
+
12
+ def self.serialize(value)
13
+ value
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ module Types
5
+ class Hash < Type
6
+ def self.cast(value)
7
+ return unless value
8
+ return value if value.is_a?(::Hash)
9
+
10
+ value.to_h
11
+ rescue TypeError, NoMethodError => e
12
+ raise ArgumentError, e
13
+ end
14
+
15
+ def self.serialize(value)
16
+ value
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ module Types
5
+ class Integer < Type
6
+ def self.cast(value)
7
+ return unless value
8
+
9
+ send :Integer, value
10
+ end
11
+
12
+ def self.serialize(value)
13
+ value
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ module Types
5
+ class String < Type
6
+ def self.cast(value)
7
+ return unless value
8
+
9
+ send :String, value
10
+ end
11
+
12
+ def self.serialize(value)
13
+ value
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kozo
4
+ module Types
5
+ class Time < Type
6
+ def self.cast(value)
7
+ return unless value
8
+ return value if value.is_a?(::Time)
9
+
10
+ ::Time.parse(value)
11
+ end
12
+
13
+ def self.serialize(value)
14
+ value&.iso8601
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/kozo/version.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Kozo
4
4
  module Version
5
5
  MAJOR = 0
6
- MINOR = 1
6
+ MINOR = 2
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
 
data/lib/kozo.rb CHANGED
@@ -1,7 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "zeitwerk"
4
3
  require "active_support/all"
4
+ require "active_model"
5
+ require "colorize"
6
+ require "dinja"
7
+ require "zeitwerk"
8
+
9
+ require "byebug" if ENV["ENV"] == "development"
5
10
 
6
11
  module Kozo
7
12
  class << self
@@ -16,11 +21,7 @@ module Kozo
16
21
  end
17
22
 
18
23
  def container
19
- @container ||= Container.new
20
- end
21
-
22
- def env
23
- @env ||= Environment.new
24
+ @container ||= Dinja::Container.new
24
25
  end
25
26
 
26
27
  def logger
@@ -30,13 +31,18 @@ module Kozo
30
31
  def setup
31
32
  @loader = Zeitwerk::Loader.for_gem
32
33
 
34
+ # Register inflections
33
35
  instance_eval(File.read(root.join("config/inflections.rb")))
34
36
 
37
+ # Set up code loader
35
38
  loader.enable_reloading
39
+ loader.ignore(root.join("lib/**/*/dependencies.rb"))
40
+ loader.collapse(root.join("lib/kozo/concerns"))
36
41
  loader.setup
37
42
  loader.eager_load
38
43
 
39
- container.instance_eval(File.read(root.join("config/dependencies.rb")))
44
+ # Register dependencies
45
+ Dir[root.join("lib/**/*/dependencies.rb"), root.join("config/dependencies.rb")].each { |d| container.instance_eval(File.read(d)) }
40
46
  end
41
47
  end
42
48
  end