producer-core 0.2.5 → 0.2.6

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/features/{actions/echo.feature → action_echo.feature} +1 -1
  3. data/features/{actions/file_append.feature → action_file_append.feature} +1 -1
  4. data/features/{actions/file_replace_content.feature → action_file_replace_content.feature} +11 -14
  5. data/features/{actions/file_write.feature → action_file_write.feature} +7 -12
  6. data/features/{actions/mkdir.feature → action_mkdir.feature} +8 -13
  7. data/features/{actions/sh.feature → action_sh.feature} +14 -14
  8. data/features/{cli/dry_run.feature → cli_dry_run.feature} +0 -0
  9. data/features/{cli/usage.feature → cli_usage.feature} +1 -0
  10. data/features/{cli/verbose.feature → cli_verbose.feature} +12 -23
  11. data/features/{recipes/ask.feature → recipe_ask.feature} +1 -0
  12. data/features/{recipes/macro.feature → recipe_macro.feature} +4 -4
  13. data/features/{recipes/source.feature → recipe_source.feature} +3 -1
  14. data/features/{recipes/target.feature → recipe_target.feature} +3 -1
  15. data/features/recipe_test_macro.feature +52 -0
  16. data/features/{recipes/registry.feature → registry.feature} +9 -4
  17. data/features/{ssh/config.feature → ssh_config.feature} +3 -1
  18. data/features/support/env.rb +32 -0
  19. data/features/support/env_aruba_timeout.rb +3 -0
  20. data/features/{tasks/condition.feature → task_condition.feature} +1 -1
  21. data/features/{tests/dir.feature → test_dir.feature} +1 -1
  22. data/features/{tests/env.feature → test_env.feature} +24 -34
  23. data/features/{tests/executable.feature → test_executable.feature} +12 -14
  24. data/features/{tests/file.feature → test_file.feature} +1 -1
  25. data/features/{tests/file_contains.feature → test_file_contains.feature} +1 -1
  26. data/features/{tests/negated_test.feature → test_negated_test.feature} +0 -0
  27. data/features/test_shell_command_status.feature +48 -0
  28. data/lib/producer/core/cli.rb +19 -15
  29. data/lib/producer/core/condition/dsl.rb +17 -8
  30. data/lib/producer/core/condition.rb +2 -2
  31. data/lib/producer/core/env.rb +6 -10
  32. data/lib/producer/core/errors.rb +2 -1
  33. data/lib/producer/core/recipe/dsl.rb +4 -0
  34. data/lib/producer/core/remote.rb +1 -1
  35. data/lib/producer/core/tests/condition_test.rb +23 -0
  36. data/lib/producer/core/version.rb +1 -1
  37. data/lib/producer/core.rb +1 -0
  38. data/spec/producer/core/cli_spec.rb +73 -44
  39. data/spec/producer/core/condition/dsl_spec.rb +33 -5
  40. data/spec/producer/core/env_spec.rb +41 -36
  41. data/spec/producer/core/recipe/dsl_spec.rb +10 -0
  42. data/spec/producer/core/remote_spec.rb +16 -7
  43. data/spec/producer/core/tests/condition_test_spec.rb +55 -0
  44. metadata +56 -55
  45. data/features/recipes/evaluation.feature +0 -9
  46. data/features/tasks/evaluation.feature +0 -11
  47. data/features/tasks/registry.feature +0 -13
  48. data/features/tests/shell_command_status.feature +0 -58
@@ -0,0 +1,3 @@
1
+ Before do
2
+ @aruba_timeout_seconds = 8
3
+ end
@@ -3,7 +3,7 @@ Feature: `condition' task keyword
3
3
  Scenario: prevents task actions application when condition is not met
4
4
  Given a recipe with:
5
5
  """
6
- task :hello do
6
+ task :some_task do
7
7
  condition { false }
8
8
 
9
9
  echo 'evaluated'
@@ -6,7 +6,7 @@ Feature: `dir?' condition keyword
6
6
  """
7
7
  target 'some_host.test'
8
8
 
9
- task :testing_directory_existence do
9
+ task :dir_test do
10
10
  condition { dir? 'some_directory' }
11
11
 
12
12
  echo 'evaluated'
@@ -1,58 +1,48 @@
1
1
  @sshd
2
2
  Feature: `env?' condition keyword
3
3
 
4
- Scenario: succeeds when remote environment variable is defined
4
+ Background:
5
5
  Given a recipe with:
6
6
  """
7
7
  target 'some_host.test'
8
8
 
9
- task :testing_env_var_definition do
9
+ task :env_test_definition_ok do
10
10
  condition { env? :shell }
11
11
 
12
- echo 'evaluated'
12
+ echo 'definition_ok'
13
13
  end
14
- """
15
- When I successfully execute the recipe
16
- Then the output must contain "evaluated"
17
14
 
18
- Scenario: fails when remote environment variable is not defined
19
- Given a recipe with:
20
- """
21
- target 'some_host.test'
22
-
23
- task :testing_env_var_definition do
15
+ task :env_test_definition_ko do
24
16
  condition { env? :non_existent_var }
25
17
 
26
- echo 'evaluated'
18
+ echo 'definition_ko'
27
19
  end
28
- """
29
- When I successfully execute the recipe
30
- Then the output must not contain "evaluated"
31
-
32
- Scenario: succeeds when remote environment variable value match
33
- Given a recipe with:
34
- """
35
- target 'some_host.test'
36
20
 
37
- task :testing_env_var_value do
21
+ task :env_test_value do
38
22
  condition { env? :shell, '/bin/sh' }
39
23
 
40
- echo 'evaluated'
24
+ echo 'value_ok'
41
25
  end
42
- """
43
- When I successfully execute the recipe
44
- Then the output must contain "evaluated"
45
26
 
46
- Scenario: fails when remote environment variable value does not match
47
- Given a recipe with:
48
- """
49
- target 'some_host.test'
50
-
51
- task :testing_env_var_value do
27
+ task :env_test_value do
52
28
  condition { env? :shell, 'non_existent_shell' }
53
29
 
54
- echo 'evaluated'
30
+ echo 'value_ko'
55
31
  end
56
32
  """
33
+
34
+ Scenario: succeeds when remote environment variable is defined
35
+ When I successfully execute the recipe
36
+ Then the output must contain "definition_ok"
37
+
38
+ Scenario: fails when remote environment variable is not defined
39
+ When I successfully execute the recipe
40
+ Then the output must not contain "definition_ko"
41
+
42
+ Scenario: succeeds when remote environment variable value match
43
+ When I successfully execute the recipe
44
+ Then the output must contain "value_ok"
45
+
46
+ Scenario: fails when remote environment variable value does not match
57
47
  When I successfully execute the recipe
58
- Then the output must not contain "evaluated"
48
+ Then the output must not contain "value_ko"
@@ -1,30 +1,28 @@
1
1
  @sshd
2
2
  Feature: `executable?' condition keyword
3
3
 
4
- Scenario: succeeds when remote executable is available
4
+ Background:
5
5
  Given a recipe with:
6
6
  """
7
7
  target 'some_host.test'
8
8
 
9
- task :testing_executable_availability do
9
+ task :executable_test_ok do
10
10
  condition { executable? 'true' }
11
11
 
12
- echo 'evaluated'
12
+ echo 'test_ok'
13
13
  end
14
- """
15
- When I successfully execute the recipe
16
- Then the output must contain "evaluated"
17
-
18
- Scenario: succeeds when remote executable is available
19
- Given a recipe with:
20
- """
21
- target 'some_host.test'
22
14
 
23
- task :testing_executable_availability do
15
+ task :executable_test_ok do
24
16
  condition { executable? 'some_non_existent_executable' }
25
17
 
26
- echo 'evaluated'
18
+ echo 'test_ko'
27
19
  end
28
20
  """
21
+
22
+ Scenario: succeeds when remote executable is available
23
+ When I successfully execute the recipe
24
+ Then the output must contain "test_ok"
25
+
26
+ Scenario: fails when remote executable is not available
29
27
  When I successfully execute the recipe
30
- Then the output must not contain "evaluated"
28
+ Then the output must not contain "test_ko"
@@ -6,7 +6,7 @@ Feature: `file?' condition keyword
6
6
  """
7
7
  target 'some_host.test'
8
8
 
9
- task :testing_file_existence do
9
+ task :file_test do
10
10
  condition { file? 'some_file' }
11
11
 
12
12
  echo 'evaluated'
@@ -6,7 +6,7 @@ Feature: `file_contains' condition keyword
6
6
  """
7
7
  target 'some_host.test'
8
8
 
9
- task :testing_content_in_file_presense do
9
+ task :file_contains_test do
10
10
  condition { file_contains 'some_file', 'some_content' }
11
11
 
12
12
  echo 'evaluated'
@@ -0,0 +1,48 @@
1
+ @sshd
2
+ Feature: `sh' and `` condition keyword
3
+
4
+ Background:
5
+ Given a recipe with:
6
+ """
7
+ target 'some_host.test'
8
+
9
+ task :sh_test_ok do
10
+ condition { sh 'true' }
11
+
12
+ echo 'test_ok'
13
+ end
14
+
15
+ task :sh_test_ko do
16
+ condition { sh 'false' }
17
+
18
+ echo 'test_ko'
19
+ end
20
+
21
+ task :sh_test_backtick_ok do
22
+ condition { `true` }
23
+
24
+ echo 'test_backtick_ok'
25
+ end
26
+
27
+ task :sh_test_backtick_ko do
28
+ condition { `false` }
29
+
30
+ echo 'test_backtick_ko'
31
+ end
32
+ """
33
+
34
+ Scenario: succeeds when remote command execution is a success
35
+ When I successfully execute the recipe
36
+ Then the output must contain "test_ok"
37
+
38
+ Scenario: fails when remote executable is not available
39
+ When I successfully execute the recipe
40
+ Then the output must not contain "test_ko"
41
+
42
+ Scenario: `` alias, succeeds when remote executable is available
43
+ When I successfully execute the recipe
44
+ Then the output must contain "test_backtick_ok"
45
+
46
+ Scenario: `` alias, fails when remote executable is not available
47
+ When I successfully execute the recipe
48
+ Then the output must not contain "test_backtick_ko"
@@ -3,36 +3,41 @@ module Producer
3
3
  class CLI
4
4
  ArgumentError = Class.new(::ArgumentError)
5
5
 
6
- USAGE = "Usage: #{File.basename $0} [-v] [-n] recipe_file"
6
+ USAGE = "Usage: #{File.basename $0} [-v] [-n] recipe_file".freeze
7
7
 
8
- EX_USAGE = 64
8
+ EX_USAGE = 64
9
+ EX_SOFTWARE = 70
9
10
 
10
11
  class << self
11
- def run!(arguments, output: $stderr)
12
+ def run!(arguments, stdin: $stdin, stdout: $stdout, stderr: $stderr)
13
+ cli = new(arguments, stdin: stdin, stdout: stdout, stderr: stderr)
12
14
  begin
13
- cli = new(arguments)
14
15
  cli.parse_arguments!
16
+ cli.run
15
17
  rescue ArgumentError
16
- output.puts USAGE
18
+ stderr.puts USAGE
17
19
  exit EX_USAGE
20
+ rescue RuntimeError => e
21
+ stderr.puts "#{e.class.name.split('::').last}: #{e.message}"
22
+ exit EX_SOFTWARE
18
23
  end
19
- cli.run
20
24
  end
21
25
  end
22
26
 
23
- attr_reader :arguments, :stdout, :env, :recipe
27
+ attr_reader :arguments, :stdin, :stdout, :stderr, :env
24
28
 
25
- def initialize(args, stdout: $stdout)
29
+ def initialize(args, stdin: $stdin, stdout: $stdout, stderr: $stderr)
26
30
  @arguments = args
31
+ @stdin = stdin
27
32
  @stdout = stdout
28
- @env = Env.new(output: stdout)
33
+ @env = Env.new(input: stdin, output: stdout)
29
34
  end
30
35
 
31
36
  def parse_arguments!
32
37
  @arguments = arguments.inject([]) do |m, e|
33
38
  case e
34
39
  when '-v'
35
- env.log_level = Logger::INFO
40
+ env.verbose = true
36
41
  when '-n'
37
42
  env.dry_run = true
38
43
  else
@@ -44,16 +49,15 @@ module Producer
44
49
  raise ArgumentError unless arguments.any?
45
50
  end
46
51
 
47
- def run(worker: build_worker)
48
- load_recipe
49
- worker.process recipe.tasks
52
+ def run
53
+ worker.process load_recipe.tasks
50
54
  end
51
55
 
52
56
  def load_recipe
53
- @recipe = Recipe.evaluate_from_file(@arguments.first, env)
57
+ Recipe.evaluate_from_file(@arguments.first, env)
54
58
  end
55
59
 
56
- def build_worker
60
+ def worker
57
61
  Worker.new(env)
58
62
  end
59
63
  end
@@ -3,12 +3,21 @@ module Producer
3
3
  class Condition
4
4
  class DSL
5
5
  class << self
6
- def define_test(keyword, klass)
7
- define_method(keyword) do |*args|
8
- @tests << klass.new(@env, *args)
9
- end
10
- define_method("no_#{keyword}") do |*args|
11
- @tests << klass.new(@env, *args, negated: true)
6
+ def define_test(keyword, test)
7
+ {
8
+ keyword => false,
9
+ "no_#{keyword}" => true
10
+ }.each do |kw, negated|
11
+ define_method(kw) do |*args|
12
+ if test.respond_to? :call
13
+ args = [test, *args]
14
+ klass = Tests::ConditionTest
15
+ else
16
+ klass = test
17
+ end
18
+ t = klass.new(@env, *args, negated: negated)
19
+ @tests << t
20
+ end
12
21
  end
13
22
  end
14
23
  end
@@ -29,8 +38,8 @@ module Producer
29
38
  @tests = []
30
39
  end
31
40
 
32
- def evaluate
33
- instance_eval &@block
41
+ def evaluate(*args)
42
+ instance_exec *args, &@block
34
43
  end
35
44
  end
36
45
  end
@@ -2,9 +2,9 @@ module Producer
2
2
  module Core
3
3
  class Condition
4
4
  class << self
5
- def evaluate(env, &block)
5
+ def evaluate(env, *args, &block)
6
6
  dsl = DSL.new(env, &block)
7
- return_value = dsl.evaluate
7
+ return_value = dsl.evaluate *args
8
8
  Condition.new(dsl.tests, return_value)
9
9
  end
10
10
  end
@@ -2,14 +2,14 @@ module Producer
2
2
  module Core
3
3
  class Env
4
4
  attr_reader :input, :output, :registry, :logger
5
- attr_accessor :target, :dry_run
5
+ attr_accessor :target, :verbose, :dry_run
6
6
 
7
7
  def initialize(input: $stdin, output: $stdout, remote: nil, registry: {})
8
+ @verbose = @dry_run = false
8
9
  @input = input
9
10
  @output = output
10
- @registry = registry
11
11
  @remote = remote
12
- @dry_run = false
12
+ @registry = registry
13
13
  end
14
14
 
15
15
  def remote
@@ -27,7 +27,7 @@ module Producer
27
27
  def logger
28
28
  @logger ||= begin
29
29
  logger = Logger.new(output)
30
- logger.level = Logger::ERROR
30
+ logger.level = verbose? ? Logger::INFO : Logger::ERROR
31
31
  logger.formatter = LoggerFormatter.new
32
32
  logger
33
33
  end
@@ -37,12 +37,8 @@ module Producer
37
37
  logger.info message
38
38
  end
39
39
 
40
- def log_level
41
- logger.level
42
- end
43
-
44
- def log_level=(level)
45
- logger.level = level
40
+ def verbose?
41
+ @verbose
46
42
  end
47
43
 
48
44
  def dry_run?
@@ -1,7 +1,8 @@
1
1
  module Producer
2
2
  module Core
3
3
  Error = Class.new(StandardError)
4
+ RuntimeError = Class.new(RuntimeError)
4
5
  ConditionNotMetError = Class.new(Error)
5
- RemoteCommandExecutionError = Class.new(Error)
6
+ RemoteCommandExecutionError = Class.new(RuntimeError)
6
7
  end
7
8
  end
@@ -38,6 +38,10 @@ module Producer
38
38
  end
39
39
  end
40
40
 
41
+ def test_macro(name, dsl: Condition::DSL, &block)
42
+ dsl.define_test(name, block)
43
+ end
44
+
41
45
  def set(key, value)
42
46
  env[key] = value
43
47
  end
@@ -32,7 +32,7 @@ module Producer
32
32
 
33
33
  ch.on_request 'exit-status' do |c, data|
34
34
  exit_status = data.read_long
35
- raise RemoteCommandExecutionError if exit_status != 0
35
+ raise RemoteCommandExecutionError, command if exit_status != 0
36
36
  end
37
37
  end
38
38
  end
@@ -0,0 +1,23 @@
1
+ module Producer
2
+ module Core
3
+ module Tests
4
+ class ConditionTest < Test
5
+ def verify
6
+ condition.met?
7
+ end
8
+
9
+ def condition
10
+ Condition.evaluate(env, *condition_args, &condition_block)
11
+ end
12
+
13
+ def condition_args
14
+ arguments.drop 1
15
+ end
16
+
17
+ def condition_block
18
+ arguments.first
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end