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
data/lib/kozo/backend.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
class Backend
|
5
|
+
attr_accessor :configuration, :directory
|
6
|
+
|
7
|
+
def initialize(configuration, directory)
|
8
|
+
@configuration = configuration
|
9
|
+
@directory = directory
|
10
|
+
end
|
11
|
+
|
12
|
+
def state
|
13
|
+
@state ||= State
|
14
|
+
.new(resources)
|
15
|
+
end
|
16
|
+
|
17
|
+
def state=(value)
|
18
|
+
@state = value
|
19
|
+
|
20
|
+
self.data = value.to_h
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate!
|
24
|
+
state_version = data.fetch(:version)
|
25
|
+
kozo_version = data.fetch(:kozo_version)
|
26
|
+
|
27
|
+
raise StateError, "invalid version in state: got #{state_version}, expected #{State::VERSION}" unless state_version == State::VERSION
|
28
|
+
|
29
|
+
return if kozo_version == Kozo::VERSION
|
30
|
+
|
31
|
+
raise StateError, "invalid kozo version in state: got #{kozo_version}, expected #{Kozo::VERSION}"
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Create necessary backend files/structures if they do not exist
|
36
|
+
#
|
37
|
+
def initialize!
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
def ==(other)
|
42
|
+
directory == other.directory
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
##
|
48
|
+
# Read state from backend
|
49
|
+
#
|
50
|
+
# @return Hash
|
51
|
+
#
|
52
|
+
def data
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Write state to backend
|
58
|
+
#
|
59
|
+
# @param value Hash
|
60
|
+
#
|
61
|
+
def data=(value)
|
62
|
+
raise NotImplementedError
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def resources
|
68
|
+
data
|
69
|
+
.fetch(:resources, [])
|
70
|
+
.map do |hash|
|
71
|
+
provider = configuration.providers[hash.dig(:meta, :provider)]
|
72
|
+
|
73
|
+
raise StateError, "provider #{hash.dig(:meta, :provider)} not configured" unless provider
|
74
|
+
|
75
|
+
Resource
|
76
|
+
.from_h(hash)
|
77
|
+
.tap { |r| r.provider = provider }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "git"
|
4
|
+
|
5
|
+
module Kozo
|
6
|
+
module Backends
|
7
|
+
class Git < Local
|
8
|
+
attr_writer :repository
|
9
|
+
attr_accessor :remote
|
10
|
+
|
11
|
+
def initialize!
|
12
|
+
Kozo.logger.debug "Initializing local state in #{repository_path}"
|
13
|
+
|
14
|
+
return if Dir.exist?(repository_path)
|
15
|
+
|
16
|
+
# Initialize git repository
|
17
|
+
@git = ::Git.init(repository_path)
|
18
|
+
git.add_remote("origin", remote) if remote
|
19
|
+
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def data=(value)
|
24
|
+
super
|
25
|
+
|
26
|
+
git.add(file)
|
27
|
+
git.commit("Update Kozo resource state")
|
28
|
+
git.push(git.remote("origin")) if remote
|
29
|
+
end
|
30
|
+
|
31
|
+
def repository
|
32
|
+
@repository ||= "kozo.git"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def repository_path
|
38
|
+
@repository_path ||= File.join(directory, repository)
|
39
|
+
end
|
40
|
+
|
41
|
+
def path
|
42
|
+
@path ||= File.join(directory, repository, file)
|
43
|
+
end
|
44
|
+
|
45
|
+
def git
|
46
|
+
@git ||= Git.open(path)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/kozo/backends/local.rb
CHANGED
@@ -1,33 +1,59 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "fileutils"
|
4
|
+
require "yaml"
|
4
5
|
|
5
6
|
module Kozo
|
6
7
|
module Backends
|
7
|
-
class Local <
|
8
|
-
|
9
|
-
|
8
|
+
class Local < Kozo::Backend
|
9
|
+
attr_writer :file
|
10
|
+
attr_accessor :backups
|
10
11
|
|
11
|
-
|
12
|
+
def initialize(configuration, directory)
|
13
|
+
super
|
12
14
|
|
13
|
-
|
14
|
-
rescue JSON::ParserError => e
|
15
|
-
Kozo.logger.fatal "Could not read state file: #{e.message}"
|
15
|
+
@directory ||= Kozo.options.directory
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
Kozo.logger.debug "
|
20
|
-
|
18
|
+
def initialize!
|
19
|
+
Kozo.logger.debug "Initializing local state in #{path}"
|
20
|
+
|
21
|
+
return if File.exist?(path)
|
22
|
+
|
23
|
+
# Initialize empty local state
|
24
|
+
self.state = State.new
|
21
25
|
end
|
22
26
|
|
23
|
-
def
|
24
|
-
|
27
|
+
def data
|
28
|
+
Kozo.logger.debug "Reading local state in #{path}"
|
29
|
+
|
30
|
+
YAML
|
31
|
+
.safe_load(File.read(path), [Time, Date])
|
32
|
+
.deep_symbolize_keys
|
33
|
+
rescue Errno::ENOENT, Errno::ENOTDIR
|
34
|
+
raise StateError, "local state at #{path} not initialized"
|
35
|
+
rescue Psych::SyntaxError => e
|
36
|
+
raise StateError, "could not read state file: #{e.message}"
|
25
37
|
end
|
26
38
|
|
27
|
-
|
39
|
+
def data=(value)
|
40
|
+
Kozo.logger.debug "Writing local state in #{path}"
|
41
|
+
|
42
|
+
@data = value
|
43
|
+
|
44
|
+
# Write backup state file
|
45
|
+
FileUtils.mv(path, "#{path}.#{DateTime.current.to_i}.kzbackup") if backups
|
46
|
+
|
47
|
+
# Write local state file
|
48
|
+
File.write(path, value.deep_stringify_keys.to_yaml)
|
49
|
+
end
|
28
50
|
|
29
51
|
def file
|
30
|
-
@file ||=
|
52
|
+
@file ||= "kozo.kzstate"
|
53
|
+
end
|
54
|
+
|
55
|
+
def path
|
56
|
+
@path ||= File.join(directory, file)
|
31
57
|
end
|
32
58
|
end
|
33
59
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Backends
|
5
|
+
class Memory < Kozo::Backend
|
6
|
+
def initialize!
|
7
|
+
Kozo.logger.debug "Initializing local state in memory"
|
8
|
+
|
9
|
+
# Initialize empty local state
|
10
|
+
self.state = State.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def data
|
14
|
+
Kozo.logger.debug "Reading local state in memory"
|
15
|
+
|
16
|
+
@data
|
17
|
+
end
|
18
|
+
|
19
|
+
def data=(value)
|
20
|
+
Kozo.logger.debug "Writing local state in memory"
|
21
|
+
|
22
|
+
@data = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/kozo/cli.rb
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "optparse"
|
4
|
+
require "English"
|
4
5
|
|
5
6
|
module Kozo
|
6
7
|
class CLI
|
7
8
|
attr_reader :parser, :args, :command_args
|
8
9
|
|
9
10
|
def initialize(args)
|
10
|
-
@parser = OptionParser.new("#{File.basename($PROGRAM_NAME)} [global options] command [command options]") do |o|
|
11
|
+
@parser = OptionParser.new("#{File.basename($PROGRAM_NAME)} [global options] command [subcommand] [command options]") do |o|
|
11
12
|
o.on("Global options:")
|
13
|
+
o.on("-d", "--directory=DIRECTORY", "Set working directory")
|
12
14
|
o.on("-v", "--verbose", "Turn on verbose logging")
|
13
15
|
o.on("-h", "--help", "Display this message") { usage }
|
14
16
|
o.separator("\n")
|
15
17
|
o.on("Commands:")
|
16
|
-
commands.each
|
18
|
+
commands.each do |(name, description, subcommands)|
|
19
|
+
o.on(" #{name.ljust(33)}#{description}")
|
20
|
+
|
21
|
+
subcommands.each do |(sb_name, sb_description)|
|
22
|
+
o.on(" #{sb_name.ljust(31)}#{sb_description}")
|
23
|
+
end
|
24
|
+
end
|
17
25
|
o.separator("\n")
|
18
26
|
end
|
19
27
|
|
@@ -37,28 +45,41 @@ module Kozo
|
|
37
45
|
def start
|
38
46
|
command = command_args.shift
|
39
47
|
|
40
|
-
|
48
|
+
raise UsageError, "no command specified" unless command
|
41
49
|
|
42
50
|
klass = "Kozo::Commands::#{command.camelize}".safe_constantize
|
43
51
|
|
44
|
-
|
52
|
+
raise UsageError, "unknown command: #{command}" unless klass
|
45
53
|
|
46
54
|
klass
|
47
|
-
.new(command_args)
|
55
|
+
.new(*command_args)
|
48
56
|
.start
|
57
|
+
rescue UsageError => e
|
58
|
+
# Don't print tail if no message was passed
|
59
|
+
return usage if e.message == e.class.name
|
60
|
+
|
61
|
+
usage(tail: "#{File.basename($PROGRAM_NAME)}: #{e.message}")
|
62
|
+
rescue Error => e
|
63
|
+
Kozo.logger.fatal e.message
|
49
64
|
end
|
50
65
|
|
51
66
|
private
|
52
67
|
|
53
68
|
def usage(code: 1, tail: nil)
|
54
|
-
|
55
|
-
|
69
|
+
Kozo.logger.info parser.to_s
|
70
|
+
Kozo.logger.info tail if tail
|
56
71
|
|
57
|
-
|
72
|
+
raise ExitError, code
|
58
73
|
end
|
59
74
|
|
60
75
|
def commands
|
61
|
-
|
76
|
+
Command.subclasses.sort_by(&:name).map do |k|
|
77
|
+
[
|
78
|
+
k.name.demodulize.underscore,
|
79
|
+
k.description,
|
80
|
+
k.descendants.sort_by(&:name).map { |s| [s.name.demodulize.underscore, s.description] },
|
81
|
+
]
|
82
|
+
end
|
62
83
|
end
|
63
84
|
end
|
64
85
|
end
|
data/lib/kozo/command.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
class Command
|
5
|
+
class_attribute :description
|
6
|
+
|
7
|
+
attr_reader :args
|
8
|
+
attr_writer :state, :configuration
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
@args = args
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def configuration
|
21
|
+
@configuration ||= Parser
|
22
|
+
.new(Kozo.options.directory)
|
23
|
+
.call
|
24
|
+
end
|
25
|
+
|
26
|
+
def state
|
27
|
+
@state ||= configuration
|
28
|
+
.backend
|
29
|
+
.state
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Commands
|
5
|
+
class Apply < Plan
|
6
|
+
self.description = "Execute a plan"
|
7
|
+
|
8
|
+
attr_reader :parser, :options
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
super()
|
12
|
+
|
13
|
+
@options = {
|
14
|
+
yes: false,
|
15
|
+
}
|
16
|
+
|
17
|
+
@parser = OptionParser.new do |o|
|
18
|
+
o.on("-y", "--yes", "Answer yes to all prompts")
|
19
|
+
end.parse!(args, into: options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
super
|
24
|
+
|
25
|
+
return if operations.empty?
|
26
|
+
|
27
|
+
unless options[:yes]
|
28
|
+
print "\nContinue executing these actions (yes/no)? ".bold
|
29
|
+
|
30
|
+
abort("\nApply cancelled") unless gets.chomp == "yes"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Apply operations to in-memory state and remote infrastructure
|
34
|
+
operations
|
35
|
+
.each { |o| o.apply(state) }
|
36
|
+
|
37
|
+
# Write state
|
38
|
+
configuration
|
39
|
+
.backend
|
40
|
+
.state = state
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Commands
|
5
|
+
class Console < Kozo::Command
|
6
|
+
self.description = "Open an interactive Ruby console"
|
7
|
+
|
8
|
+
def start
|
9
|
+
# rubocop:disable Lint/Debugger
|
10
|
+
binding.irb
|
11
|
+
# rubocop:enable Lint/Debugger
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Commands
|
5
|
+
class Import < Kozo::Command
|
6
|
+
self.description = "Import existing resources into the state"
|
7
|
+
|
8
|
+
attr_reader :address, :id
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
address = args.shift
|
12
|
+
|
13
|
+
raise UsageError, "address not specified" unless address
|
14
|
+
|
15
|
+
id = args.shift
|
16
|
+
|
17
|
+
raise UsageError, "id not specified" unless id
|
18
|
+
|
19
|
+
@address = address
|
20
|
+
@id = id
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
# Find resource in configuration
|
25
|
+
resource = configuration
|
26
|
+
.resources
|
27
|
+
.find { |r| r.address == address }
|
28
|
+
|
29
|
+
raise StateError, "no such resource address: #{address}" unless resource
|
30
|
+
|
31
|
+
# Set ID and fetch attributes
|
32
|
+
resource
|
33
|
+
.tap { |r| r.id = id }
|
34
|
+
.refresh!
|
35
|
+
|
36
|
+
# Add or replace resource to in-memory state
|
37
|
+
state
|
38
|
+
.resources << resource
|
39
|
+
|
40
|
+
# Write state
|
41
|
+
configuration
|
42
|
+
.backend
|
43
|
+
.state = state
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Commands
|
5
|
+
class Init < Kozo::Command
|
6
|
+
self.description = "Prepare your working directory for other commands"
|
7
|
+
|
8
|
+
def start
|
9
|
+
configuration.backend.initialize!
|
10
|
+
|
11
|
+
Kozo.logger.info "Kozo initialized in #{configuration.directory}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/kozo/commands/plan.rb
CHANGED
@@ -2,11 +2,37 @@
|
|
2
2
|
|
3
3
|
module Kozo
|
4
4
|
module Commands
|
5
|
-
class Plan <
|
6
|
-
self.description = "
|
5
|
+
class Plan < Kozo::Command
|
6
|
+
self.description = "Create and show an execution plan"
|
7
|
+
|
8
|
+
attr_reader :operations
|
9
|
+
|
10
|
+
def initialize(*_args)
|
11
|
+
@operations = []
|
12
|
+
end
|
7
13
|
|
8
14
|
def start
|
9
|
-
configuration.
|
15
|
+
@operations += configuration.changes.filter_map do |resource|
|
16
|
+
if resource.marked_for_creation?
|
17
|
+
Operations::Create.new(resource)
|
18
|
+
elsif resource.marked_for_deletion?
|
19
|
+
Operations::Destroy.new(resource)
|
20
|
+
elsif resource.changed?
|
21
|
+
Operations::Update.new(resource)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Kozo.logger.info "Kozo analyzed the state and created the following execution plan. Actions are indicated by the following symbols:"
|
26
|
+
|
27
|
+
[:create, :update, :destroy]
|
28
|
+
.map { |c| "Kozo::Operations::#{c.to_s.camelize}".constantize }
|
29
|
+
.each { |o| Kozo.logger.info " #{o.display_symbol} #{o.name.demodulize.downcase}" }
|
30
|
+
|
31
|
+
return Kozo.logger.info "\nNo actions have to be performed." if operations.empty?
|
32
|
+
|
33
|
+
Kozo.logger.info "\nKozo will perform the following actions:"
|
34
|
+
|
35
|
+
operations.each { |o| Kozo.logger.info o.to_s }
|
10
36
|
end
|
11
37
|
end
|
12
38
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Commands
|
5
|
+
class Refresh < Kozo::Command
|
6
|
+
self.description = "Update the state to match remote infrastructure"
|
7
|
+
|
8
|
+
def start
|
9
|
+
# Refresh resources in-memory
|
10
|
+
state
|
11
|
+
.resources
|
12
|
+
.each(&:refresh!)
|
13
|
+
|
14
|
+
# Write state
|
15
|
+
configuration
|
16
|
+
.backend
|
17
|
+
.state = state
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Commands
|
5
|
+
class Show < Kozo::Command
|
6
|
+
self.description = "Show all resources in the state"
|
7
|
+
|
8
|
+
def start
|
9
|
+
state
|
10
|
+
.resources
|
11
|
+
.each { |r| Kozo.logger.info Operations::Show.new(r).to_s }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Commands
|
5
|
+
class State < Kozo::Command
|
6
|
+
self.description = "Manage and manipulate state"
|
7
|
+
|
8
|
+
attr_reader :subcommand
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
subcommand = args.shift
|
12
|
+
|
13
|
+
raise UsageError unless subcommand
|
14
|
+
|
15
|
+
klass = "Kozo::Commands::State::#{subcommand.camelize}".safe_constantize
|
16
|
+
|
17
|
+
raise UsageError, "unknown subcommand: state #{subcommand}" unless klass
|
18
|
+
|
19
|
+
@subcommand = klass.new(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
subcommand
|
24
|
+
.start
|
25
|
+
end
|
26
|
+
|
27
|
+
class List < State
|
28
|
+
self.description = "List resources in the state"
|
29
|
+
|
30
|
+
def initialize(*_args); end
|
31
|
+
|
32
|
+
def start
|
33
|
+
state
|
34
|
+
.resources
|
35
|
+
.each { |r| Kozo.logger.info r.address }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Show < State
|
40
|
+
self.description = "Show a resource in the state"
|
41
|
+
|
42
|
+
attr_reader :address
|
43
|
+
|
44
|
+
def initialize(*args)
|
45
|
+
address = args.shift
|
46
|
+
|
47
|
+
raise UsageError, "address not specified" unless address
|
48
|
+
|
49
|
+
@address = address
|
50
|
+
end
|
51
|
+
|
52
|
+
def start
|
53
|
+
resource = state
|
54
|
+
.resources
|
55
|
+
.find { |r| r.address == address }
|
56
|
+
|
57
|
+
raise StateError, "no such resource address: #{address}" unless resource
|
58
|
+
|
59
|
+
Kozo.logger.info Operations::Show.new(resource).to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Commands
|
5
|
+
class Validate < Kozo::Command
|
6
|
+
self.description = "Check whether configuration is valid"
|
7
|
+
|
8
|
+
def start
|
9
|
+
Kozo.logger.info "The configuration in #{configuration.directory} is valid"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kozo
|
4
|
+
module Assignment
|
5
|
+
def initialize(attributes = {})
|
6
|
+
super()
|
7
|
+
|
8
|
+
assign_attributes(attributes) if attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
def assign_attributes(attributes)
|
12
|
+
attributes.each do |k, v|
|
13
|
+
send(:"#{k}=", v)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|