producer-core 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
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
-