diff --git a/features/actions/file_write.feature b/features/actions/file_write.feature new file mode 100644 index 0000000..eb4682e --- /dev/null +++ b/features/actions/file_write.feature @@ -0,0 +1,15 @@ +@sshd +Feature: `file_write' task action + + Scenario: writes given data to given file path + Given a recipe with: + """ + target 'some_host.test' + + task :write_some_data do + file_write 'some_file', 'some_content' + end + """ + When I execute the recipe + Then the exit status must be 0 + And the remote file "some_file" must contain "some_content" diff --git a/features/steps/remote_steps.rb b/features/steps/remote_steps.rb index 644baf4..427a5a9 100644 --- a/features/steps/remote_steps.rb +++ b/features/steps/remote_steps.rb @@ -1,3 +1,7 @@ -Given(/^a remote file named "(.*?)"$/) do |file_name| +Given /^a remote file named "([^"]+)"$/ do |file_name| write_file file_name, '' end + +Then /^the remote file "([^"]+)" should contain "([^"]+)"/ do |path, content| + check_file_content path, content, true +end diff --git a/lib/producer/core.rb b/lib/producer/core.rb index 6735974..7a7a3f3 100644 --- a/lib/producer/core.rb +++ b/lib/producer/core.rb @@ -2,6 +2,7 @@ require 'producer/core/action' require 'producer/core/actions/echo' require 'producer/core/actions/shell_command' +require 'producer/core/actions/file_writer' # condition tests (need to be defined before the condition DSL) require 'producer/core/test' diff --git a/lib/producer/core/actions/file_writer.rb b/lib/producer/core/actions/file_writer.rb new file mode 100644 index 0000000..dd2f192 --- /dev/null +++ b/lib/producer/core/actions/file_writer.rb @@ -0,0 +1,19 @@ +module Producer + module Core + module Actions + class FileWriter < Action + def apply + env.remote.fs.file_write path, content + end + + def path + arguments[0] + end + + def content + arguments[1] + end + end + end + end +end diff --git a/lib/producer/core/remote/fs.rb b/lib/producer/core/remote/fs.rb index 14a8b34..19603f5 100644 --- a/lib/producer/core/remote/fs.rb +++ b/lib/producer/core/remote/fs.rb @@ -17,6 +17,12 @@ module Producer rescue Net::SFTP::StatusException false end + + def file_write(path, content) + sftp.file.open path, 'w' do |f| + f.write content + end + end end end end diff --git a/lib/producer/core/task/dsl.rb b/lib/producer/core/task/dsl.rb index 4d4b545..a231acc 100644 --- a/lib/producer/core/task/dsl.rb +++ b/lib/producer/core/task/dsl.rb @@ -19,6 +19,8 @@ module Producer define_action :echo, Actions::Echo define_action :sh, Actions::ShellCommand + define_action :file_write, Actions::FileWriter + attr_accessor :actions def initialize(&block) diff --git a/spec/producer/core/actions/file_writer_spec.rb b/spec/producer/core/actions/file_writer_spec.rb new file mode 100644 index 0000000..60fa8e8 --- /dev/null +++ b/spec/producer/core/actions/file_writer_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +module Producer::Core + describe Actions::FileWriter do + let(:env) { Env.new } + let(:path) { 'some_path' } + let(:content) { 'some_content' } + subject(:writer) { Actions::FileWriter.new(env, path, content) } + + describe '#apply' do + it 'delegates the call to env.remote.fs.file_write method' do + expect(env.remote.fs).to receive(:file_write).with(path, content) + writer.apply + end + end + + describe '#path' do + it 'returns the path' do + expect(writer.path).to eq path + end + end + + describe '#content' do + it 'returns the content' do + expect(writer.content).to eq content + end + end + end +end diff --git a/spec/producer/core/remote/fs_spec.rb b/spec/producer/core/remote/fs_spec.rb index 5cb45dc..4709b96 100644 --- a/spec/producer/core/remote/fs_spec.rb +++ b/spec/producer/core/remote/fs_spec.rb @@ -72,5 +72,30 @@ module Producer::Core end end end + + describe '#file_write' do + let(:sftp) { double('sftp').as_null_object } + let(:file) { double('sftp').as_null_object } + let(:path) { 'some_file_path' } + let(:content) { 'some_content' } + + before do + allow(fs).to receive(:sftp) { sftp } + allow(sftp).to receive(:file) { file } + end + + it 'opens the file' do + expect(file).to receive(:open).with(path, 'w') + fs.file_write path, content + end + + it 'writes the content' do + expect(file).to receive(:open).with(any_args) do |&b| + expect(file).to receive(:write).with(content) + b.call file + end + fs.file_write path, content + end + end end end diff --git a/spec/producer/core/remote_spec.rb b/spec/producer/core/remote_spec.rb index 10c129a..39b88eb 100644 --- a/spec/producer/core/remote_spec.rb +++ b/spec/producer/core/remote_spec.rb @@ -94,8 +94,7 @@ module Producer::Core ch.sends_exec command ch.gets_data arguments end - remote.execute command - expect(story_completed?).to be + expect_story_completed { remote.execute command } end it 'returns the output' do diff --git a/spec/producer/core/task/dsl_spec.rb b/spec/producer/core/task/dsl_spec.rb index f8b424a..0529043 100644 --- a/spec/producer/core/task/dsl_spec.rb +++ b/spec/producer/core/task/dsl_spec.rb @@ -6,7 +6,7 @@ module Producer::Core let(:env) { double 'env' } subject(:dsl) { Task::DSL.new(&block) } - %w[echo sh].each do |action| + %w[echo sh file_write].each do |action| it "has `#{action}' action defined" do expect(dsl).to respond_to action.to_sym end diff --git a/spec/support/net_ssh_story_helpers.rb b/spec/support/net_ssh_story_helpers.rb index 5d2931f..a386503 100644 --- a/spec/support/net_ssh_story_helpers.rb +++ b/spec/support/net_ssh_story_helpers.rb @@ -29,10 +29,6 @@ module NetSSHStoryHelpers end end - def story_completed? - socket.script.events.empty? - end - def sftp_story story do |session| ch = session.opens_channel @@ -48,4 +44,11 @@ module NetSSHStoryHelpers yield ch if block_given? end end + + def expect_story_completed + raise 'there is no story to expect' if socket.script.events.empty? + yield + expect(socket.script.events) + .to be_empty, "#{socket.script.events.count} story events still pending" + end end