avm 0.6.0 → 0.9.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/avm/data/package/dump.rb +3 -3
  3. data/lib/avm/data/rotate.rb +107 -0
  4. data/lib/avm/docker/runner.rb +8 -0
  5. data/lib/avm/instances/application.rb +25 -0
  6. data/lib/avm/instances/base/auto_values/access.rb +40 -0
  7. data/lib/avm/instances/base/auto_values/admin.rb +19 -0
  8. data/lib/avm/instances/base/auto_values/data.rb +26 -0
  9. data/lib/avm/instances/base/auto_values/database.rb +76 -0
  10. data/lib/avm/instances/base/auto_values/filesystem.rb +45 -0
  11. data/lib/avm/instances/base/auto_values/mailer.rb +34 -0
  12. data/lib/avm/instances/base/auto_values/ruby.rb +15 -0
  13. data/lib/avm/instances/base/auto_values/source.rb +15 -0
  14. data/lib/avm/instances/base/auto_values/system.rb +23 -0
  15. data/lib/avm/instances/base/auto_values/web.rb +46 -0
  16. data/lib/avm/instances/base/auto_values.rb +21 -0
  17. data/lib/avm/instances/base/dockerizable.rb +45 -0
  18. data/lib/avm/instances/base/entry_keys.rb +22 -0
  19. data/lib/avm/instances/base.rb +64 -0
  20. data/lib/avm/instances/entries.rb +43 -0
  21. data/lib/avm/instances/entry.rb +54 -0
  22. data/lib/avm/instances/entry_keys.rb +57 -0
  23. data/lib/avm/instances/runner.rb +38 -0
  24. data/lib/avm/instances.rb +8 -0
  25. data/lib/avm/jobs/base.rb +1 -0
  26. data/lib/avm/registry/base.rb +1 -1
  27. data/lib/avm/registry.rb +1 -1
  28. data/lib/avm/runners/base.rb +31 -0
  29. data/lib/avm/self/docker_image.rb +14 -0
  30. data/lib/avm/self/instance/entry_keys.rb +12 -0
  31. data/lib/avm/self/instance.rb +24 -0
  32. data/lib/avm/source_stereotypes/base.rb +16 -2
  33. data/lib/avm/source_stereotypes/tester.rb +52 -0
  34. data/lib/avm/sources/base/configuration.rb +37 -0
  35. data/lib/avm/sources/base.rb +5 -8
  36. data/lib/avm/sources/tests/builder.rb +83 -0
  37. data/lib/avm/sources/tests/performer.rb +35 -0
  38. data/lib/avm/sources/tests/result.rb +15 -0
  39. data/lib/avm/sources/tests/single.rb +56 -0
  40. data/lib/avm/sources/tests.rb +11 -0
  41. data/lib/avm/version.rb +1 -1
  42. metadata +54 -2
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/require_sub'
4
+ require 'eac_ruby_utils/simple_cache'
5
+ require 'avm/instances/entries'
6
+
7
+ module Avm
8
+ module Instances
9
+ class Base
10
+ enable_listable
11
+ enable_simple_cache
12
+ require_sub __FILE__, include_modules: true
13
+ include ::Avm::Instances::Entries
14
+
15
+ lists.add_string :access, :local, :ssh
16
+
17
+ ID_PATTERN = /\A([a-z0-9]+(?:\-[a-z0-9]+)*)_(.+)\z/.freeze
18
+
19
+ class << self
20
+ def by_id(id)
21
+ application_id, suffix = parse_id(id)
22
+ require 'avm/instances/application'
23
+ new(::Avm::Instances::Application.new(application_id), suffix)
24
+ end
25
+
26
+ private
27
+
28
+ def parse_id(id)
29
+ m = ID_PATTERN.match(id)
30
+ return [m[1], m[2]] if m
31
+
32
+ raise "ID Pattern no matched: \"#{id}\""
33
+ end
34
+ end
35
+
36
+ common_constructor :application, :suffix do
37
+ self.suffix = suffix.to_s
38
+ end
39
+
40
+ def id
41
+ "#{application.id}_#{suffix}"
42
+ end
43
+
44
+ def to_s
45
+ id
46
+ end
47
+
48
+ def host_env_uncached
49
+ access = read_entry(:access, list: ::Avm::Instances::Base.lists.access.values)
50
+ case access
51
+ when 'local' then ::EacRubyUtils::Envs.local
52
+ when 'ssh' then ::EacRubyUtils::Envs.ssh(read_entry('ssh.url'))
53
+ else raise("Unmapped access value: \"#{access}\"")
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def source_instance_uncached
60
+ ::Avm::Instances::Base.by_id(source_instance_id)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+ require 'avm/instances/entry'
5
+
6
+ module Avm
7
+ module Instances
8
+ module Entries
9
+ def entry(suffix, options = {})
10
+ ::Avm::Instances::Entry.new(self, suffix, options)
11
+ end
12
+
13
+ def path_prefix
14
+ @path_prefix ||= [id].freeze
15
+ end
16
+
17
+ def read_entry(entry_suffix, options = {})
18
+ entry(entry_suffix, options).value
19
+ end
20
+
21
+ def read_entry_optional(entry_suffix, options = {})
22
+ entry(entry_suffix, options).optional_value
23
+ end
24
+
25
+ def full_entry_path(entry_suffix)
26
+ unless entry_suffix.is_a?(::Array)
27
+ entry_suffix = ::EacConfig::PathsHash.parse_entry_key(entry_suffix.to_s)
28
+ end
29
+ (path_prefix + entry_suffix).join('.')
30
+ end
31
+
32
+ def inherited_entry_value(source_entry_suffix, target_entry_suffix, &block)
33
+ read_entry_optional(source_entry_suffix).if_present do |instance_id|
34
+ other_entry_value(instance_id, target_entry_suffix).if_present(&block)
35
+ end
36
+ end
37
+
38
+ def other_entry_value(instance_id, entry_suffix)
39
+ ::Avm::Instances::Base.by_id(instance_id).read_entry_optional(entry_suffix)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_config/node'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Instances
8
+ class Entry
9
+ class << self
10
+ def auto_value_method_name(suffix)
11
+ "auto_#{suffix.to_s.gsub('.', '_')}"
12
+ end
13
+ end
14
+
15
+ common_constructor :parent, :suffix, :options
16
+
17
+ def auto_value
18
+ parent.respond_to?(auto_value_method, true) ? parent.send(auto_value_method) : nil
19
+ end
20
+
21
+ def auto_value_method
22
+ self.class.auto_value_method_name(suffix)
23
+ end
24
+
25
+ def full_path
26
+ (parent.path_prefix + suffix_as_array).join('.')
27
+ end
28
+
29
+ def optional_value
30
+ read(required: false, noinput: true) || auto_value
31
+ end
32
+
33
+ def read(extra_options = {})
34
+ ::EacConfig::Node.context.current.entry(full_path, options.merge(extra_options)).value
35
+ end
36
+
37
+ def suffix_as_array
38
+ if suffix.is_a?(::Array)
39
+ suffix.dup
40
+ else
41
+ ::EacConfig::PathsHash.parse_entry_key(suffix.to_s)
42
+ end
43
+ end
44
+
45
+ def value
46
+ optional_value || read
47
+ end
48
+
49
+ def write(value)
50
+ ::EacConfig::Node.context.current.entry(full_path).value = value
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module Instances
7
+ module EntryKeys
8
+ class << self
9
+ def all
10
+ all_keys.to_a
11
+ end
12
+
13
+ def keys_consts_set(prefix, suffixes)
14
+ if suffixes.is_a?(::Hash)
15
+ keys_consts_set_from_hash(prefix, suffixes)
16
+ elsif suffixes.is_a?(::Enumerable)
17
+ keys_consts_set_from_enum(prefix, suffixes)
18
+ else
19
+ raise "Unmapped suffixes class: #{suffixes.class}"
20
+ end
21
+ end
22
+
23
+ def key_const_set(prefix, suffix)
24
+ key = [prefix, suffix].reject(&:blank?).join('.')
25
+ const_set(key.gsub('.', '_').upcase, key)
26
+ all_keys << key
27
+ end
28
+
29
+ private
30
+
31
+ def all_keys
32
+ @all_keys ||= ::Set.new
33
+ end
34
+
35
+ def keys_consts_set_from_enum(prefix, suffixes)
36
+ suffixes.each { |suffix| key_const_set(prefix, suffix) }
37
+ end
38
+
39
+ def keys_consts_set_from_hash(prefix, suffixes)
40
+ suffixes.each { |k, v| keys_consts_set(prefix.to_s + (k.blank? ? '' : ".#{k}"), v) }
41
+ end
42
+ end
43
+
44
+ {
45
+ '' => %w[fs_path host_id source_instance_id],
46
+ database: %w[id hostname limit name password port system timeout username extra],
47
+ docker: %w[registry],
48
+ mailer: {
49
+ '' => %w[id from reply_to],
50
+ smtp: %w[address port domain username password authentication starttls_auto]
51
+ },
52
+ ssh: %w[hostname port url username],
53
+ web: %w[authority hostname path port scheme url userinfo]
54
+ }.each { |prefix, suffixes| keys_consts_set(prefix, suffixes) }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/core_ext'
4
+
5
+ module Avm
6
+ module Instances
7
+ class Runner
8
+ class << self
9
+ def instance_class
10
+ ::Avm.const_get(stereotype_name).const_get('Instance')
11
+ end
12
+
13
+ def stereotype_module
14
+ ::Avm.const_get(stereotype_name)
15
+ end
16
+
17
+ def stereotype_name
18
+ name.demodulize
19
+ end
20
+ end
21
+
22
+ description = "Utilities for #{stereotype_name} instances."
23
+ runner_with :help, :subcommands do
24
+ desc description
25
+ pos_arg 'instance-id'
26
+ subcommands
27
+ end
28
+
29
+ delegate :instance_class, :stereotype_module, :stereotype_name, to: :class
30
+
31
+ private
32
+
33
+ def instance_uncached
34
+ self.class.instance_class.by_id(parsed.instance_id)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Avm
4
+ module Instances
5
+ require 'avm/instances/application'
6
+ require 'avm/instances/entries'
7
+ end
8
+ end
data/lib/avm/jobs/base.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'avm/jobs/variables_source'
4
+ require 'avm/result'
4
5
  require 'eac_cli/core_ext'
5
6
 
6
7
  module Avm
@@ -12,7 +12,7 @@ module Avm
12
12
  def detect(*registered_initialize_args)
13
13
  detect_optional(*registered_initialize_args) ||
14
14
  raise("No registered module valid for #{registered_initialize_args}" \
15
- " (Module suffix: #{module_suffix})")
15
+ " (Module suffix: #{module_suffix}, Available: #{registered_modules.join(', ')})")
16
16
  end
17
17
 
18
18
  def detect_optional(*registered_initialize_args)
data/lib/avm/registry.rb CHANGED
@@ -7,7 +7,7 @@ module Avm
7
7
  module Registry
8
8
  require_sub __FILE__
9
9
  enable_listable
10
- lists.add_symbol :category, :instance_stereotypes, :scms, :source_stereotypes
10
+ lists.add_symbol :category, :instance_stereotypes, :runners, :scms, :source_stereotypes
11
11
 
12
12
  class << self
13
13
  enable_simple_cache
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/core_ext'
4
+
5
+ module Avm
6
+ module Runners
7
+ class Base
8
+ enable_abstract_methods
9
+
10
+ class << self
11
+ def command_argument
12
+ stereotype_name.underscore.dasherize
13
+ end
14
+
15
+ def stereotype_name
16
+ name.split('::')[-3]
17
+ end
18
+ end
19
+
20
+ delegate :command_argument, :stereotype_name, to: :class
21
+
22
+ runner_with :help, :subcommands do
23
+ subcommands
24
+ end
25
+
26
+ def to_s
27
+ stereotype_name
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+ require 'avm/docker/image'
5
+
6
+ module Avm
7
+ module Self
8
+ class DockerImage < ::Avm::Docker::Image
9
+ def stereotype_tag
10
+ nil
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Avm
4
+ module Self
5
+ class Instance < ::Avm::Instances::Base
6
+ module EntryKeys
7
+ DATA_DEFAULT_PATH = 'data.default_path'
8
+ DOCKER_REGISTRY_NAME = 'docker.registry.name'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/instances/base'
4
+ require 'avm/self/docker_image'
5
+ require 'avm/self/instance/entry_keys'
6
+ require 'avm/eac_ubuntu_base0/docker_image'
7
+
8
+ module Avm
9
+ module Self
10
+ class Instance < ::Avm::Instances::Base
11
+ def docker_image_class
12
+ ::Avm::Self::DockerImage
13
+ end
14
+
15
+ def docker_registry
16
+ read_entry(::Avm::Self::Instance::EntryKeys::DOCKER_REGISTRY_NAME)
17
+ end
18
+
19
+ def docker_run_arguments
20
+ ['-e', "LOCAL_USER_ID=#{::Process.uid}"]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,21 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'avm/source_stereotypes/tester'
3
4
  require 'eac_ruby_utils/core_ext'
5
+ require 'eac_ruby_utils/envs'
4
6
 
5
7
  module Avm
6
8
  module SourceStereotypes
7
9
  class Base
8
- enable_abstract_methods
9
- abstract_methods :valid?
10
10
  common_constructor :source
11
11
 
12
12
  def name
13
13
  self.class.name
14
14
  end
15
15
 
16
+ # @return [Avm::SourceStereotypes::Tester]
17
+ def tester
18
+ tester_class.new(self)
19
+ end
20
+
21
+ # @return [Class<Avm::SourceStereotypes::Tester>
22
+ def tester_class
23
+ Avm::SourceStereotypes::Tester
24
+ end
25
+
16
26
  def to_s
17
27
  name
18
28
  end
29
+
30
+ def valid?
31
+ true
32
+ end
19
33
  end
20
34
  end
21
35
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module SourceStereotypes
7
+ class Tester
8
+ TEST_COMMAND_CONFIGURATION_KEY = :test_command
9
+
10
+ enable_simple_cache
11
+ common_constructor :source_stereotype
12
+ delegate :source, to: :source_stereotype
13
+
14
+ # @return [EacRubyUtils::Envs::Command, nil]
15
+ def test_command
16
+ source.read_configuration_as_shell_words(TEST_COMMAND_CONFIGURATION_KEY)
17
+ .if_present do |args|
18
+ ::EacRubyUtils::Envs.local.command(args).chdir(source.path)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def logs_uncached
25
+ ::EacRubyUtils::Fs::Logs.new.add(:stdout).add(:stderr)
26
+ end
27
+
28
+ # @return [Avm::Sources::Tests::Result]
29
+ def result_uncached
30
+ return ::Avm::Sources::Tests::Result::NONEXISTENT if test_command.blank?
31
+
32
+ if execute_command_and_log(test_command)
33
+ ::Avm::Sources::Tests::Result::SUCESSFUL
34
+ else
35
+ ::Avm::Sources::Tests::Result::FAILED
36
+ end
37
+ end
38
+
39
+ def run_test_command
40
+ execute_command_and_log(test_command)
41
+ end
42
+
43
+ # @return [true, false]
44
+ def execute_command_and_log(command)
45
+ r = command.execute
46
+ logs[:stdout].write(r[:stdout])
47
+ logs[:stderr].write(r[:stderr])
48
+ r[:exit_code].zero?
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+ require 'eac_ruby_utils/yaml'
5
+ require 'shellwords'
6
+
7
+ module Avm
8
+ module Sources
9
+ class Base
10
+ module Configuration
11
+ # @return [Array<String>, nil]
12
+ def read_configuration_as_shell_words(key)
13
+ configuration[key].if_present do |v|
14
+ v.is_a?(::Enumerable) ? v.map(&:to_s) : ::Shellwords.split(v.to_s)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ # @return [Hash]
21
+ def configuration_uncached
22
+ ::Avm::Sources::Configuration::FILENAMES.each do |filename|
23
+ file_path = path.join(filename)
24
+ return ::EacRubyUtils::Yaml.load_file(file_path).with_indifferent_access if
25
+ file_path.exist?
26
+ end
27
+ {}
28
+ end
29
+
30
+ # @return [Avm::Sources::Configuration]
31
+ def old_configuration_uncached
32
+ ::Avm::Sources::Configuration.find_in_path(path)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -7,17 +7,19 @@ require 'eac_ruby_utils/core_ext'
7
7
  module Avm
8
8
  module Sources
9
9
  class Base
10
- require_sub __FILE__
10
+ require_sub __FILE__, include_modules: true
11
+ compare_by :path
11
12
  enable_simple_cache
12
13
  enable_listable
13
14
  lists.add_symbol :option, :parent
14
15
  common_constructor :path, :options, default: [{}] do
15
- self.path = path.to_pathname
16
+ self.path = path.to_pathname.expand_path
16
17
  self.options = self.class.lists.option.hash_keys_validate!(options)
17
18
  end
18
19
 
19
- delegate :locale, to: :configuration
20
+ delegate :locale, to: :old_configuration
20
21
  delegate :to_s, to: :path
22
+ delegate :tester, to: :stereotype
21
23
 
22
24
  # @return [Avm::Sources::Base]
23
25
  def parent
@@ -43,11 +45,6 @@ module Avm
43
45
 
44
46
  private
45
47
 
46
- # @return [Avm::Sources::Configuration]
47
- def configuration_uncached
48
- ::Avm::Sources::Configuration.find_in_path(path) || ::Avm::Sources::Configuration.new
49
- end
50
-
51
48
  # @return [EacGit::Local]
52
49
  def git_repo_uncached
53
50
  ::EacGit::Local.new(path)
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/sources/tests/performer'
4
+ require 'avm/sources/tests/single'
5
+ require 'eac_ruby_utils/core_ext'
6
+
7
+ module Avm
8
+ module Sources
9
+ module Tests
10
+ class Builder
11
+ require_sub __FILE__
12
+ enable_immutable
13
+
14
+ immutable_accessor :include_main, :include_subs, type: :boolean
15
+ immutable_accessor :include_id, type: :array
16
+ common_constructor :main_source
17
+
18
+ def immutable_constructor_args
19
+ [main_source]
20
+ end
21
+
22
+ # @return [Avm::Sources::Tests::Performer]
23
+ def performer
24
+ ::Avm::Sources::Tests::Performer.new(self)
25
+ end
26
+
27
+ def selected_units
28
+ (select_units_from_subs + select_units_from_main + select_units_from_ids).sort.uniq
29
+ end
30
+
31
+ private
32
+
33
+ # @return [Array<Avm::Sources::Tests::Single>]
34
+ def available_units
35
+ @available_units ||= ([main_source] + main_source.subs)
36
+ .map { |a_source| create_unit(a_source) }
37
+ end
38
+
39
+ def available_units_from_main
40
+ create_units([main_source])
41
+ end
42
+
43
+ def available_units_from_subs
44
+ create_units(main_source.subs)
45
+ end
46
+
47
+ # @return [Avm::Sources::Tests::Single]
48
+ def create_unit(source)
49
+ ::Avm::Sources::Tests::Single.new(self, source)
50
+ end
51
+
52
+ # @return [Array<Avm::Sources::Tests::Single>]
53
+ def create_units(sources)
54
+ sources.map { |a_source| create_unit(a_source) }
55
+ end
56
+
57
+ # @return [Avm::Sources::Tests::Single]
58
+ def create_unit_by_id(source_id)
59
+ r = available_units.find { |unit| unit.id == source_id }
60
+ return r if r
61
+
62
+ raise ::ArgumentError, "Source not found with ID=#{source_id}" \
63
+ "(Available: #{available_units.map(&:id).join(', ')})"
64
+ end
65
+
66
+ # @return [Array<Avm::Sources::Tests::Single>]
67
+ def select_units_from_ids
68
+ include_ids.map { |source_id| create_unit_by_id(source_id) }
69
+ end
70
+
71
+ # @return [Array<Avm::Sources::Tests::Single>]
72
+ def select_units_from_main
73
+ include_main? ? available_units_from_main : []
74
+ end
75
+
76
+ # @return [Array<Avm::Sources::Tests::Single>]
77
+ def select_units_from_subs
78
+ include_subs? ? available_units_from_subs : []
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/sources/tests/result'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Sources
8
+ module Tests
9
+ class Performer
10
+ enable_simple_cache
11
+
12
+ common_constructor(:owner)
13
+
14
+ def non_failed_units
15
+ units.reject(&:failed?)
16
+ end
17
+
18
+ def failed_units
19
+ units.select(&:failed?)
20
+ end
21
+
22
+ # @return [Boolean]
23
+ def successful?
24
+ failed_units.none?
25
+ end
26
+
27
+ protected
28
+
29
+ def units_uncached
30
+ owner.selected_units
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/enum'
4
+
5
+ module Avm
6
+ module Sources
7
+ module Tests
8
+ class Result < ::EacCli::Enum
9
+ enum :failed, :red
10
+ enum :nonexistent, :white
11
+ enum :sucessful, :green
12
+ end
13
+ end
14
+ end
15
+ end