producer-core 0.1.12 → 0.1.13

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/Guardfile +4 -3
  3. data/features/actions/file_replace_content.feature +31 -0
  4. data/features/recipes/registry.feature +21 -0
  5. data/features/steps/remote_steps.rb +8 -0
  6. data/features/tasks/registry.feature +13 -0
  7. data/features/tests/file_contains.feature +28 -0
  8. data/lib/producer/core/action.rb +0 -2
  9. data/lib/producer/core/actions/file_replace_content.rb +27 -0
  10. data/lib/producer/core/condition/dsl.rb +4 -3
  11. data/lib/producer/core/env.rb +15 -5
  12. data/lib/producer/core/recipe/dsl.rb +8 -0
  13. data/lib/producer/core/remote/environment.rb +0 -2
  14. data/lib/producer/core/remote/fs.rb +9 -9
  15. data/lib/producer/core/remote.rb +1 -4
  16. data/lib/producer/core/task/dsl.rb +7 -2
  17. data/lib/producer/core/test.rb +0 -2
  18. data/lib/producer/core/testing/mock_remote.rb +25 -0
  19. data/lib/producer/core/testing.rb +1 -0
  20. data/lib/producer/core/tests/file_contains.rb +18 -0
  21. data/lib/producer/core/version.rb +1 -1
  22. data/lib/producer/core.rb +9 -0
  23. data/producer-core.gemspec +2 -2
  24. data/spec/producer/core/action_spec.rb +1 -42
  25. data/spec/producer/core/actions/echo_spec.rb +11 -8
  26. data/spec/producer/core/actions/file_replace_content_spec.rb +49 -0
  27. data/spec/producer/core/actions/file_writer_spec.rb +20 -17
  28. data/spec/producer/core/actions/mkdir_spec.rb +15 -13
  29. data/spec/producer/core/actions/shell_command_spec.rb +16 -14
  30. data/spec/producer/core/condition/dsl_spec.rb +53 -51
  31. data/spec/producer/core/env_spec.rb +30 -2
  32. data/spec/producer/core/prompter_spec.rb +0 -1
  33. data/spec/producer/core/recipe/dsl_spec.rb +82 -66
  34. data/spec/producer/core/remote/environment_spec.rb +32 -30
  35. data/spec/producer/core/remote/fs_spec.rb +100 -104
  36. data/spec/producer/core/remote_spec.rb +4 -13
  37. data/spec/producer/core/task/dsl_spec.rb +82 -72
  38. data/spec/producer/core/task_spec.rb +1 -1
  39. data/spec/producer/core/test_spec.rb +1 -77
  40. data/spec/producer/core/testing/mock_remote_spec.rb +46 -0
  41. data/spec/producer/core/tests/file_contains_spec.rb +46 -0
  42. data/spec/producer/core/tests/has_dir_spec.rb +15 -18
  43. data/spec/producer/core/tests/has_env_spec.rb +34 -34
  44. data/spec/producer/core/tests/has_file_spec.rb +15 -18
  45. data/spec/spec_helper.rb +3 -3
  46. data/spec/support/shared_action.rb +44 -0
  47. data/spec/support/shared_test.rb +82 -0
  48. data/spec/support/test_env_helpers.rb +34 -0
  49. metadata +33 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ed15ccec37cb38bc7c4ea42a911708acd30630f
4
- data.tar.gz: bc36933dae78ef8311eb2e840ab22cbb2927dd11
3
+ metadata.gz: 5075625b8dd7462b61363dfca8186442ecf2df57
4
+ data.tar.gz: fa274fc269efe57af84d363b116d248778b60756
5
5
  SHA512:
6
- metadata.gz: 9128d8070fb39fa4cc1e4a6ba1ea9a19c29ac32b2088a412d2da5b66a5560b3b6bf9332a689423d246f936eb6aa9552b0e97a131679e11670048be996f194ceb
7
- data.tar.gz: 1be0962d9d254e3f33a04636b66413084f957541456d7202cf120da1085d4cd33b856dcb464532356188e9123e77127be1ffa390a22caf3e283df3afd2919d99
6
+ metadata.gz: 7cebd961ba388776c216f61c5c7acbeb1b764b9140e59dffe4ea14d0859e446e0ff7b9b56d643b671bcc364333d2d62dc8b110ce20d8fd257e33958684815e29
7
+ data.tar.gz: 501ed6a4b3782ea91b689331a99b7fade6037e0e5bf0120dfb1f95384b3d8544e2dc69916ee4083fdc7788cd4b5ce597b8be57abd1a766a33e676cd981745502
data/Guardfile CHANGED
@@ -4,8 +4,9 @@ guard :cucumber, cli: '--format pretty --quiet' do
4
4
  watch(%r{\Afeatures/step_definitions/.+_steps\.rb\z}) { 'features' }
5
5
  end
6
6
 
7
- guard :rspec, cmd: 'rspec -f doc', all_after_pass: true do
7
+ guard :rspec, cmd: 'rspec -f doc' do
8
8
  watch(%r{\Aspec/.+_spec\.rb\z})
9
- watch(%r{\Alib/(.+)\.rb\z}) { |m| "spec/#{m[1]}_spec.rb" }
10
- watch('spec/spec_helper.rb') { 'spec' }
9
+ watch(%r{\Alib/(.+)\.rb\z}) { |m| "spec/#{m[1]}_spec.rb" }
10
+ watch('spec/spec_helper.rb') { 'spec' }
11
+ watch(%r{\Aspec/support/.+\.rb\z}) { 'spec' }
11
12
  end
@@ -0,0 +1,31 @@
1
+ @sshd
2
+ Feature: `file_replace_content' task action
3
+
4
+ Background:
5
+ Given a remote file named "some_file" with "some content"
6
+
7
+ Scenario: replace a string by another in the requested file
8
+ Given a recipe with:
9
+ """
10
+ target 'some_host.test'
11
+
12
+ task :replace_string_in_file do
13
+ file_replace_content 'some_file', 'content', 'other content'
14
+ end
15
+ """
16
+ When I execute the recipe
17
+ Then the exit status must be 0
18
+ And the remote file "some_file" must contain exactly "some other content"
19
+
20
+ Scenario: replace a regular expression by a string in the requested file
21
+ Given a recipe with:
22
+ """
23
+ target 'some_host.test'
24
+
25
+ task :replace_string_in_file do
26
+ file_replace_content 'some_file', /\w+\z/, 'other content'
27
+ end
28
+ """
29
+ When I execute the recipe
30
+ Then the exit status must be 0
31
+ And the remote file "some_file" must contain exactly "some other content"
@@ -0,0 +1,21 @@
1
+ Feature: key/value registry
2
+
3
+ Scenario: `set' keyword registers a value in the registry
4
+ Given a recipe with:
5
+ """
6
+ set :some_key, 'some_value'
7
+
8
+ puts env.registry[:some_key]
9
+ """
10
+ When I successfully execute the recipe
11
+ Then the output must contain "some_value"
12
+
13
+ Scenario: `get' keyword fetches a value from the registry
14
+ Given a recipe with:
15
+ """
16
+ set :some_key, 'some_value'
17
+
18
+ puts get :some_key
19
+ """
20
+ When I successfully execute the recipe
21
+ Then the output must contain "some_value"
@@ -6,6 +6,10 @@ Given /^a remote file named "([^"]+)"$/ do |file_name|
6
6
  write_file file_name, ''
7
7
  end
8
8
 
9
+ Given /^a remote file named "([^"]+)" with "([^"]+)"$/ do |file_name, content|
10
+ write_file file_name, content
11
+ end
12
+
9
13
  Then /^the remote directory "([^"]+)" should exists$/ do |path|
10
14
  check_directory_presence([path], true)
11
15
  end
@@ -13,3 +17,7 @@ end
13
17
  Then /^the remote file "([^"]+)" should contain "([^"]+)"/ do |path, content|
14
18
  check_file_content path, content, true
15
19
  end
20
+
21
+ Then /^the remote file "([^"]+)" should contain exactly "([^"]+)"/ do |path, content|
22
+ check_exact_file_content path, content
23
+ end
@@ -0,0 +1,13 @@
1
+ Feature: access to registry from task DSL
2
+
3
+ Scenario: `get' keyword fetches a value from the registry
4
+ Given a recipe with:
5
+ """
6
+ set :some_key, 'some_value'
7
+
8
+ task :output_some_key do
9
+ echo get :some_key
10
+ end
11
+ """
12
+ When I successfully execute the recipe
13
+ Then the output must contain "some_value"
@@ -0,0 +1,28 @@
1
+ @sshd
2
+ Feature: `file_contains' condition keyword
3
+
4
+ Background:
5
+ Given a recipe with:
6
+ """
7
+ target 'some_host.test'
8
+
9
+ task :testing_content_in_file_presense do
10
+ condition { file_contains 'some_file', 'some_content' }
11
+
12
+ echo 'evaluated'
13
+ end
14
+ """
15
+
16
+ Scenario: succeeds when file contains expected content
17
+ Given a remote file named "some_file" with "some_content"
18
+ When I successfully execute the recipe
19
+ Then the output must contain "evaluated"
20
+
21
+ Scenario: fails when file does not contain expected content
22
+ Given a remote file named "some_file" with "some_other_content"
23
+ When I successfully execute the recipe
24
+ Then the output must not contain "evaluated"
25
+
26
+ Scenario: fails when file does not exist
27
+ When I successfully execute the recipe
28
+ Then the output must not contain "evaluated"
@@ -1,8 +1,6 @@
1
1
  module Producer
2
2
  module Core
3
3
  class Action
4
- require 'forwardable'
5
-
6
4
  extend Forwardable
7
5
  def_delegators :@env, :input, :output, :remote
8
6
  def_delegators :remote, :fs
@@ -0,0 +1,27 @@
1
+ module Producer
2
+ module Core
3
+ module Actions
4
+ class FileReplaceContent < Action
5
+ def apply
6
+ fs.file_write path, replaced_content
7
+ end
8
+
9
+ def path
10
+ arguments[0]
11
+ end
12
+
13
+ def pattern
14
+ arguments[1]
15
+ end
16
+
17
+ def replacement
18
+ arguments[2]
19
+ end
20
+
21
+ def replaced_content
22
+ fs.file_read(path).gsub pattern, replacement
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -13,9 +13,10 @@ module Producer
13
13
  end
14
14
  end
15
15
 
16
- define_test :has_env, Tests::HasEnv
17
- define_test :has_dir, Tests::HasDir
18
- define_test :has_file, Tests::HasFile
16
+ define_test :file_contains, Tests::FileContains
17
+ define_test :has_env, Tests::HasEnv
18
+ define_test :has_dir, Tests::HasDir
19
+ define_test :has_file, Tests::HasFile
19
20
 
20
21
  attr_reader :block, :env, :tests
21
22
 
@@ -1,18 +1,28 @@
1
1
  module Producer
2
2
  module Core
3
3
  class Env
4
- attr_reader :input, :output
4
+ attr_reader :input, :output, :registry
5
5
  attr_accessor :target
6
6
 
7
- def initialize(input: $stdin, output: $stdout)
8
- @input = input
9
- @output = output
10
- @target = nil
7
+ def initialize(input: $stdin, output: $stdout, remote: nil, registry: {})
8
+ @input = input
9
+ @output = output
10
+ @registry = registry
11
+ @remote = remote
12
+ @target = nil
11
13
  end
12
14
 
13
15
  def remote
14
16
  @remote ||= Remote.new(target)
15
17
  end
18
+
19
+ def [](key)
20
+ @registry[key]
21
+ end
22
+
23
+ def []=(key, value)
24
+ @registry[key] = value
25
+ end
16
26
  end
17
27
  end
18
28
  end
@@ -37,6 +37,14 @@ module Producer
37
37
  task("#{name}", *args, &block)
38
38
  end
39
39
  end
40
+
41
+ def set(key, value)
42
+ env[key] = value
43
+ end
44
+
45
+ def get(key)
46
+ env[key]
47
+ end
40
48
  end
41
49
  end
42
50
  end
@@ -12,8 +12,6 @@ module Producer
12
12
  end
13
13
  end
14
14
 
15
- require 'forwardable'
16
-
17
15
  extend Forwardable
18
16
  def_delegators :@variables, :[], :key?
19
17
 
@@ -2,16 +2,10 @@ module Producer
2
2
  module Core
3
3
  class Remote
4
4
  class FS
5
- require 'net/sftp'
5
+ attr_reader :sftp
6
6
 
7
- attr_reader :remote
8
-
9
- def initialize(remote)
10
- @remote = remote
11
- end
12
-
13
- def sftp
14
- @sftp ||= @remote.session.sftp.connect
7
+ def initialize(sftp)
8
+ @sftp = sftp
15
9
  end
16
10
 
17
11
  def dir?(path)
@@ -30,6 +24,12 @@ module Producer
30
24
  sftp.mkdir! path
31
25
  end
32
26
 
27
+ def file_read(path)
28
+ sftp.file.open(path) { |f| content = f.read }
29
+ rescue Net::SFTP::StatusException
30
+ nil
31
+ end
32
+
33
33
  def file_write(path, content)
34
34
  sftp.file.open path, 'w' do |f|
35
35
  f.write content
@@ -1,9 +1,6 @@
1
1
  module Producer
2
2
  module Core
3
3
  class Remote
4
- require 'etc'
5
- require 'net/ssh'
6
-
7
4
  attr_reader :hostname
8
5
 
9
6
  def initialize(hostname)
@@ -23,7 +20,7 @@ module Producer
23
20
  end
24
21
 
25
22
  def fs
26
- @fs ||= Remote::FS.new(self)
23
+ @fs ||= Remote::FS.new(session.sftp.connect)
27
24
  end
28
25
 
29
26
  def execute(command)
@@ -13,8 +13,9 @@ module Producer
13
13
  define_action :echo, Actions::Echo
14
14
  define_action :sh, Actions::ShellCommand
15
15
 
16
- define_action :mkdir, Actions::Mkdir
17
- define_action :file_write, Actions::FileWriter
16
+ define_action :mkdir, Actions::Mkdir
17
+ define_action :file_write, Actions::FileWriter
18
+ define_action :file_replace_content, Actions::FileReplaceContent
18
19
 
19
20
  attr_reader :env, :block, :actions
20
21
 
@@ -37,6 +38,10 @@ module Producer
37
38
  def ask(question, choices, prompter: Prompter)
38
39
  prompter.new(env.input, env.output).prompt(question, choices)
39
40
  end
41
+
42
+ def get(key)
43
+ env[key]
44
+ end
40
45
  end
41
46
  end
42
47
  end
@@ -1,8 +1,6 @@
1
1
  module Producer
2
2
  module Core
3
3
  class Test
4
- require 'forwardable'
5
-
6
4
  extend Forwardable
7
5
  def_delegators :@env, :remote
8
6
  def_delegators :remote, :fs
@@ -0,0 +1,25 @@
1
+ module Producer
2
+ module Core
3
+ module Testing
4
+ class MockRemote < Remote
5
+ def session
6
+ raise 'no session for mock remote!'
7
+ end
8
+
9
+ def execute(command)
10
+ tokens = command.split
11
+ program = tokens.shift
12
+
13
+ case program
14
+ when 'echo'
15
+ tokens.join ' '
16
+ when 'true'
17
+ ''
18
+ when 'false'
19
+ raise RemoteCommandExecutionError
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1 @@
1
+ require 'producer/core/testing/mock_remote'
@@ -0,0 +1,18 @@
1
+ module Producer
2
+ module Core
3
+ module Tests
4
+ class FileContains < Test
5
+ def verify
6
+ content = file_content
7
+ content ? content.include?(arguments[1]) : false
8
+ end
9
+
10
+ private
11
+
12
+ def file_content
13
+ fs.file_read(arguments[0])
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  module Producer
2
2
  module Core
3
- VERSION = '0.1.12'
3
+ VERSION = '0.1.13'
4
4
  end
5
5
  end
data/lib/producer/core.rb CHANGED
@@ -1,12 +1,21 @@
1
+ require 'forwardable'
2
+
3
+ require 'etc'
4
+ require 'net/ssh'
5
+ require 'net/sftp'
6
+
7
+
1
8
  # task actions
2
9
  require 'producer/core/action'
3
10
  require 'producer/core/actions/echo'
4
11
  require 'producer/core/actions/shell_command'
5
12
  require 'producer/core/actions/mkdir'
13
+ require 'producer/core/actions/file_replace_content'
6
14
  require 'producer/core/actions/file_writer'
7
15
 
8
16
  # condition tests (need to be defined before the condition DSL)
9
17
  require 'producer/core/test'
18
+ require 'producer/core/tests/file_contains'
10
19
  require 'producer/core/tests/has_dir'
11
20
  require 'producer/core/tests/has_env'
12
21
  require 'producer/core/tests/has_file'
@@ -17,8 +17,8 @@ Gem::Specification.new do |s|
17
17
  s.executables = s.files.grep(/\Abin\//) { |f| File.basename(f) }
18
18
 
19
19
 
20
- s.add_dependency 'net-ssh'
21
- s.add_dependency 'net-sftp'
20
+ s.add_dependency 'net-ssh', '~> 2.7.0'
21
+ s.add_dependency 'net-sftp', '~> 2.1.2'
22
22
 
23
23
  s.add_development_dependency 'rspec'
24
24
  s.add_development_dependency 'cucumber'
@@ -2,47 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  module Producer::Core
4
4
  describe Action do
5
- let(:input) { StringIO.new }
6
- let(:output) { StringIO.new }
7
- let(:env) { Env.new(input: input, output: output) }
8
- let(:arguments) { [:some, :arguments] }
9
- subject(:action) { Action.new(env, *arguments) }
10
-
11
- describe '#env' do
12
- it 'returns the assigned env' do
13
- expect(action.env).to be env
14
- end
15
- end
16
-
17
- describe '#arguments' do
18
- it 'returns the assigned arguments' do
19
- expect(action.arguments).to eq arguments
20
- end
21
- end
22
-
23
- describe '#input' do
24
- it 'returns env input' do
25
- expect(action.input).to be input
26
- end
27
- end
28
-
29
- describe '#output' do
30
- it 'delegates to env output' do
31
- action.output.puts 'some content'
32
- expect(output.string).to eq "some content\n"
33
- end
34
- end
35
-
36
- describe '#remote' do
37
- it 'returns env remote' do
38
- expect(action.remote).to be action.env.remote
39
- end
40
- end
41
-
42
- describe '#fs' do
43
- it 'returns env remote fs' do
44
- expect(action.fs).to be action.env.remote.fs
45
- end
46
- end
5
+ it_behaves_like 'action'
47
6
  end
48
7
  end
@@ -1,15 +1,18 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Producer::Core
4
- describe Actions::Echo do
5
- let(:env) { Env.new(output: StringIO.new) }
6
- let(:text) { 'hello' }
7
- subject(:echo) { Actions::Echo.new(env, text) }
4
+ module Actions
5
+ describe Echo, :env do
6
+ let(:text) { 'hello' }
7
+ subject(:echo) { Echo.new(env, text) }
8
8
 
9
- describe '#apply' do
10
- it 'writes the given string to env output with a record separator' do
11
- echo.apply
12
- expect(env.output.string).to eq "hello\n"
9
+ it_behaves_like 'action'
10
+
11
+ describe '#apply' do
12
+ it 'writes the given string to env output with a record separator' do
13
+ echo.apply
14
+ expect(output).to eq "hello\n"
15
+ end
13
16
  end
14
17
  end
15
18
  end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ module Producer::Core
4
+ module Actions
5
+ describe FileReplaceContent, :env do
6
+ let(:path) { 'some_path' }
7
+ let(:pattern) { 'content' }
8
+ let(:replacement) { 'other content' }
9
+ let(:content) { 'some content' }
10
+ subject(:action) { FileReplaceContent.new(env, path, pattern, replacement) }
11
+
12
+ it_behaves_like 'action'
13
+
14
+ before { allow(remote_fs).to receive(:file_read).with(path) { content } }
15
+
16
+ describe '#apply' do
17
+ it 'writes replaced content to file on remote filesystem' do
18
+ expect(remote_fs)
19
+ .to receive(:file_write).with(path, action.replaced_content)
20
+ action.apply
21
+ end
22
+ end
23
+
24
+ describe '#path' do
25
+ it 'returns the file path' do
26
+ expect(action.path).to eq path
27
+ end
28
+ end
29
+
30
+ describe '#pattern' do
31
+ it 'returns the pattern' do
32
+ expect(action.pattern).to eq pattern
33
+ end
34
+ end
35
+
36
+ describe '#replacement' do
37
+ it 'returns the replacement' do
38
+ expect(action.replacement).to eq replacement
39
+ end
40
+ end
41
+
42
+ describe '#replaced_content' do
43
+ it 'returns content with pattern occurrences pattern replaced' do
44
+ expect(action.replaced_content).to eq 'some other content'
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,28 +1,31 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Producer::Core
4
- describe Actions::FileWriter do
5
- let(:env) { Env.new }
6
- let(:path) { 'some_path' }
7
- let(:content) { 'some_content' }
8
- subject(:writer) { Actions::FileWriter.new(env, path, content) }
4
+ module Actions
5
+ describe FileWriter, :env do
6
+ let(:path) { 'some_path' }
7
+ let(:content) { 'some_content' }
8
+ subject(:writer) { FileWriter.new(env, path, content) }
9
9
 
10
- describe '#apply' do
11
- it 'writes content to file on remote filesystem' do
12
- expect(writer.fs).to receive(:file_write).with(path, content)
13
- writer.apply
10
+ it_behaves_like 'action'
11
+
12
+ describe '#apply' do
13
+ it 'writes content to file on remote filesystem' do
14
+ expect(remote_fs).to receive(:file_write).with(path, content)
15
+ writer.apply
16
+ end
14
17
  end
15
- end
16
18
 
17
- describe '#path' do
18
- it 'returns the path' do
19
- expect(writer.path).to eq path
19
+ describe '#path' do
20
+ it 'returns the path' do
21
+ expect(writer.path).to eq path
22
+ end
20
23
  end
21
- end
22
24
 
23
- describe '#content' do
24
- it 'returns the content' do
25
- expect(writer.content).to eq content
25
+ describe '#content' do
26
+ it 'returns the content' do
27
+ expect(writer.content).to eq content
28
+ end
26
29
  end
27
30
  end
28
31
  end
@@ -1,23 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Producer::Core
4
- describe Actions::Mkdir do
5
- let(:env) { Env.new }
6
- let(:path) { 'some_path' }
7
- subject(:mkdir) { Actions::Mkdir.new(env, path) }
4
+ module Actions
5
+ describe Mkdir, :env do
6
+ let(:path) { 'some_path' }
7
+ subject(:mkdir) { Mkdir.new(env, path) }
8
8
 
9
- describe '#apply' do
10
- it 'creates directory on remote filesystem' do
11
- expect(mkdir.fs).to receive(:mkdir).with(path)
12
- mkdir.apply
9
+ it_behaves_like 'action'
10
+
11
+ describe '#apply' do
12
+ it 'creates directory on remote filesystem' do
13
+ expect(remote_fs).to receive(:mkdir).with(path)
14
+ mkdir.apply
15
+ end
13
16
  end
14
- end
15
17
 
16
- describe '#path' do
17
- it 'returns the path' do
18
- expect(mkdir.path).to eq path
18
+ describe '#path' do
19
+ it 'returns the path' do
20
+ expect(mkdir.path).to eq path
21
+ end
19
22
  end
20
23
  end
21
24
  end
22
25
  end
23
-