itamae-spec 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +7 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +42 -0
  7. data/Rakefile +47 -0
  8. data/bin/itamae-spec +4 -0
  9. data/itamae-spec.gemspec +36 -0
  10. data/lib/itamae-spec.rb +10 -0
  11. data/lib/itamae-spec/cli.rb +19 -0
  12. data/lib/itamae-spec/generators.rb +20 -0
  13. data/lib/itamae-spec/generators/cookbook.rb +10 -0
  14. data/lib/itamae-spec/generators/project.rb +10 -0
  15. data/lib/itamae-spec/generators/templates/cookbook/attributes/.keep +0 -0
  16. data/lib/itamae-spec/generators/templates/cookbook/recipes/default.rb +0 -0
  17. data/lib/itamae-spec/generators/templates/cookbook/recipes/files/.keep +0 -0
  18. data/lib/itamae-spec/generators/templates/cookbook/recipes/templates/.keep +0 -0
  19. data/lib/itamae-spec/generators/templates/cookbook/spec/default_spec.rb +0 -0
  20. data/lib/itamae-spec/generators/templates/project/.rspec +2 -0
  21. data/lib/itamae-spec/generators/templates/project/Gemfile +3 -0
  22. data/lib/itamae-spec/generators/templates/project/Project.json +1 -0
  23. data/lib/itamae-spec/generators/templates/project/Rakefile +9 -0
  24. data/lib/itamae-spec/generators/templates/project/cookbooks/sample/attributes/.keep +0 -0
  25. data/lib/itamae-spec/generators/templates/project/cookbooks/sample/attributes/default.json +5 -0
  26. data/lib/itamae-spec/generators/templates/project/cookbooks/sample/recipes/default.rb +7 -0
  27. data/lib/itamae-spec/generators/templates/project/cookbooks/sample/recipes/files/.keep +0 -0
  28. data/lib/itamae-spec/generators/templates/project/cookbooks/sample/recipes/templates/.keep +0 -0
  29. data/lib/itamae-spec/generators/templates/project/cookbooks/sample/spec/default_spec.rb +9 -0
  30. data/lib/itamae-spec/generators/templates/project/environments/.keep +0 -0
  31. data/lib/itamae-spec/generators/templates/project/environments/sample.json +7 -0
  32. data/lib/itamae-spec/generators/templates/project/keys/.keep +0 -0
  33. data/lib/itamae-spec/generators/templates/project/nodes/.keep +0 -0
  34. data/lib/itamae-spec/generators/templates/project/nodes/sample.json +10 -0
  35. data/lib/itamae-spec/generators/templates/project/roles/.keep +0 -0
  36. data/lib/itamae-spec/generators/templates/project/roles/sample.json +5 -0
  37. data/lib/itamae-spec/generators/templates/project/spec/spec_helper.rb +41 -0
  38. data/lib/itamae-spec/generators/templates/project/tmp-nodes/.keep +0 -0
  39. data/lib/itamae-spec/logger.rb +76 -0
  40. data/lib/itamae-spec/resource.rb +2 -0
  41. data/lib/itamae-spec/resource/http_request.rb +71 -0
  42. data/lib/itamae-spec/resource/s3_file.rb +31 -0
  43. data/lib/itamae-spec/task/base.rb +90 -0
  44. data/lib/itamae-spec/task/base_task.rb +148 -0
  45. data/lib/itamae-spec/task/itamae_task.rb +112 -0
  46. data/lib/itamae-spec/task/local_itamae_task.rb +84 -0
  47. data/lib/itamae-spec/task/local_serverspec_task.rb +125 -0
  48. data/lib/itamae-spec/task/serverspec_task.rb +111 -0
  49. data/lib/itamae-spec/version.rb +3 -0
  50. data/lib/itamae-spec/version.txt +1 -0
  51. data/spec/integration/Vagrantfile +35 -0
  52. data/spec/integration/default_spec.rb +226 -0
  53. data/spec/integration/recipes/default.rb +423 -0
  54. data/spec/integration/recipes/default2.rb +6 -0
  55. data/spec/integration/recipes/define/default.rb +6 -0
  56. data/spec/integration/recipes/define/files/remote_file_in_definition +1 -0
  57. data/spec/integration/recipes/dry_run.rb +6 -0
  58. data/spec/integration/recipes/files/remote_file_auto +1 -0
  59. data/spec/integration/recipes/hello.erb +6 -0
  60. data/spec/integration/recipes/hello.txt +1 -0
  61. data/spec/integration/recipes/included.rb +9 -0
  62. data/spec/integration/recipes/node.json +3 -0
  63. data/spec/integration/recipes/redefine.rb +20 -0
  64. data/spec/integration/recipes/templates/template_auto.erb +6 -0
  65. data/spec/integration/spec_helper.rb +42 -0
  66. data/spec/unit/lib/itamae/backend_spec.rb +95 -0
  67. data/spec/unit/lib/itamae/handler/base_spec.rb +34 -0
  68. data/spec/unit/lib/itamae/handler/fluentd_spec.rb +19 -0
  69. data/spec/unit/lib/itamae/handler_proxy_spec.rb +38 -0
  70. data/spec/unit/lib/itamae/handler_spec.rb +11 -0
  71. data/spec/unit/lib/itamae/node_spec.rb +14 -0
  72. data/spec/unit/lib/itamae/recipe_spec.rb +6 -0
  73. data/spec/unit/lib/itamae/resource/base_spec.rb +127 -0
  74. data/spec/unit/lib/itamae/resource_spec.rb +23 -0
  75. data/spec/unit/lib/itamae/runner_spec.rb +32 -0
  76. data/spec/unit/spec_helper.rb +23 -0
  77. metadata +315 -0
@@ -0,0 +1,6 @@
1
+ <%= node['greeting'] %>
2
+ <%= @goodbye %>
3
+
4
+ total memory: <%= node['memory']['total'] %>
5
+
6
+ uninitialized node key: <%= node['un-initialized'] %>
@@ -0,0 +1 @@
1
+ Hello Itamae
@@ -0,0 +1,9 @@
1
+ require 'pathname'
2
+ included_flag_file = Pathname.new("/tmp/included_rb_is_included")
3
+ if included_flag_file.exist? && included_flag_file.read == $$.to_s
4
+ raise "included.rb should not be included twice."
5
+ else
6
+ included_flag_file.write($$.to_s)
7
+ end
8
+
9
+ execute "touch /tmp/included_recipe"
@@ -0,0 +1,3 @@
1
+ {
2
+ "greeting": "Hello"
3
+ }
@@ -0,0 +1,20 @@
1
+ # spec/integration/recipes/redefine.rb
2
+ define :echo_hello, version: nil do
3
+ file "put file in redefine.rb" do
4
+ action :create
5
+ path "/tmp/created_in_redefine"
6
+ content 'first'
7
+ end
8
+ end
9
+
10
+ # Duplicated definitions
11
+ define :echo_hello, version: nil do
12
+ file "put file in redefine.rb" do
13
+ action :create
14
+ path "/tmp/created_in_redefine"
15
+ content 'second'
16
+ end
17
+ end
18
+
19
+ # Execute
20
+ echo_hello "execute echo_hello!"
@@ -0,0 +1,6 @@
1
+ <%= node['greeting'] %>
2
+ <%= @goodbye %>
3
+
4
+ total memory: <%= node['memory']['total'] %>
5
+
6
+ uninitialized node key: <%= node['un-initialized'] %>
@@ -0,0 +1,42 @@
1
+ require 'serverspec'
2
+ require 'net/ssh'
3
+ require 'tempfile'
4
+
5
+ set :backend, :ssh
6
+
7
+ def vagrant(cmd)
8
+ env = {"VAGRANT_CWD" => File.dirname(__FILE__)}
9
+ system(env, "vagrant #{cmd}")
10
+ end
11
+
12
+ if ENV['ASK_SUDO_PASSWORD']
13
+ begin
14
+ require 'highline/import'
15
+ rescue LoadError
16
+ fail "highline is not available. Try installing it."
17
+ end
18
+ set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
19
+ else
20
+ set :sudo_password, ENV['SUDO_PASSWORD']
21
+ end
22
+
23
+ host = ENV['TARGET_HOST']
24
+
25
+ config = Tempfile.new('', Dir.tmpdir)
26
+ vagrant "ssh-config #{host} > #{config.path}"
27
+
28
+ options = Net::SSH::Config.for(host, [config.path])
29
+
30
+ options[:user] ||= Etc.getlogin
31
+
32
+ set :host, options[:host_name] || host
33
+ set :ssh_options, options
34
+
35
+ # Disable sudo
36
+ # set :disable_sudo, true
37
+
38
+ # Set environment variables
39
+ # set :env, :LANG => 'C', :LC_MESSAGES => 'C'
40
+
41
+ # Set PATH
42
+ # set :path, '/sbin:/usr/local/sbin:$PATH'
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+ require 'fakefs/spec_helpers'
3
+
4
+ module Itamae
5
+ module Backend
6
+ describe Base do
7
+ include FakeFS::SpecHelpers
8
+
9
+ class Klass < Itamae::Backend::Base
10
+ def initialize(_, backend)
11
+ @backend = backend
12
+ end
13
+ end
14
+
15
+ let(:backend) { double('backend', send_file: nil, send_directory: nil) }
16
+ let(:itamae_backend) { Klass.new('dummy', backend) }
17
+
18
+ describe ".send_file" do
19
+ context "the source file doesn't exist" do
20
+ subject { -> { itamae_backend.send_file("src", "dst") } }
21
+ it { expect(subject).to raise_error(Itamae::Backend::SourceNotExistError, "The file 'src' doesn't exist.") }
22
+ end
23
+
24
+ context "the source file exist, but it is not a regular file" do
25
+ before { Dir.mkdir("src") }
26
+ subject { -> { itamae_backend.send_file("src", "dst") } }
27
+ it { expect(subject).to raise_error(Itamae::Backend::SourceNotExistError, "'src' is not a file.") }
28
+ end
29
+
30
+ context "the source file is a regular file" do
31
+ before { FileUtils.touch("src") }
32
+ subject { -> { itamae_backend.send_file("src", "dst") } }
33
+ it { expect { subject }.not_to raise_error }
34
+ end
35
+ end
36
+
37
+ describe ".send_directory" do
38
+ context "the source directory doesn't exist" do
39
+ subject { -> { itamae_backend.send_directory("src", "dst") } }
40
+ it { expect(subject).to raise_error(Itamae::Backend::SourceNotExistError, "The directory 'src' doesn't exist.") }
41
+ end
42
+
43
+ context "the source directory exist, but it is not a directory" do
44
+ before { FileUtils.touch("src") }
45
+ subject { -> { itamae_backend.send_directory("src", "dst") } }
46
+ it { expect(subject).to raise_error(Itamae::Backend::SourceNotExistError, "'src' is not a directory.") }
47
+ end
48
+
49
+ context "the source directory is a directory" do
50
+ before { Dir.mkdir("src") }
51
+ subject { -> { itamae_backend.send_directory("src", "dst") } }
52
+ it { expect { subject }.not_to raise_error }
53
+ end
54
+ end
55
+ end
56
+
57
+ describe Ssh do
58
+
59
+ describe "#ssh_options" do
60
+ subject { ssh.send(:ssh_options) }
61
+
62
+ let!(:ssh) { described_class.new(options) }
63
+ let!(:host_name) { "example.com" }
64
+ let!(:default_option) do
65
+ opts = {}
66
+ opts[:host_name] = nil
67
+ opts.merge!(Net::SSH::Config.for(host_name))
68
+ opts[:user] = opts[:user] || Etc.getlogin
69
+ opts
70
+ end
71
+
72
+ context "with host option" do
73
+ let(:options) { {host: host_name} }
74
+ it { is_expected.to eq( default_option.merge({host_name: host_name}) ) }
75
+ end
76
+ end
77
+
78
+ describe "#disable_sudo?" do
79
+ subject { ssh.send(:disable_sudo?) }
80
+
81
+ let!(:ssh) { described_class.new(options)}
82
+
83
+ context "when sudo option is true" do
84
+ let(:options) { {sudo: true} }
85
+ it { is_expected.to eq(false) }
86
+ end
87
+
88
+ context "when sudo option is false" do
89
+ let(:options) { {sudo: false} }
90
+ it { is_expected.to eq(true) }
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Itamae::Handler::Base do
4
+ subject(:handler) { described_class.new({}) }
5
+
6
+ context "when receiving recipe_started event" do
7
+ it "stores the payload" do
8
+ subject.event(:recipe_started, :payload)
9
+ expect(subject.recipes).to eq([:payload])
10
+ end
11
+ end
12
+
13
+ context "when receiving recipe_completed event" do
14
+ before do
15
+ subject.event(:recipe_started, :payload)
16
+ end
17
+
18
+ it "pops the payload" do
19
+ subject.event(:recipe_completed, :payload)
20
+ expect(subject.recipes).to eq([])
21
+ end
22
+ end
23
+
24
+ context "when receiving recipe_failed event" do
25
+ before do
26
+ subject.event(:recipe_started, :payload)
27
+ end
28
+
29
+ it "pops the payload" do
30
+ subject.event(:recipe_failed, :payload)
31
+ expect(subject.recipes).to eq([])
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'itamae/handler/fluentd'
3
+
4
+ describe Itamae::Handler::Fluentd do
5
+ subject(:handler) do
6
+ described_class.new(options).tap do |h|
7
+ h.fluent_logger = fluent_logger
8
+ end
9
+ end
10
+ let(:options) { {'hostname' => 'me'} }
11
+ let(:fluent_logger) { Fluent::Logger::TestLogger.new }
12
+
13
+ describe '#event' do
14
+ it 'posts a record to fluent logger' do
15
+ subject.event(:type, {arg: 'value'})
16
+ expect(fluent_logger.queue).to eq([{arg: 'value', hostname: 'me'}])
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ module Itamae
4
+ describe HandlerProxy do
5
+ let(:handler) { instance_double(Handler::Base) }
6
+ before { subject.register_instance(handler) }
7
+
8
+ describe "#event" do
9
+ context "with block" do
10
+ context "when the block completes" do
11
+ it "fires *_started and *_completed events" do
12
+ expect(handler).to receive(:event).with(:name_started, :arg)
13
+ expect(handler).to receive(:event).with(:name_completed, :arg)
14
+ subject.event(:name, :arg) { }
15
+ end
16
+ end
17
+
18
+ context "when the block fails" do
19
+ it "fires *_started and *_failed events" do
20
+ expect(handler).to receive(:event).with(:name_started, :arg)
21
+ expect(handler).to receive(:event).with(:name_failed, :arg)
22
+ expect {
23
+ subject.event(:name, :arg) { raise }
24
+ }.to raise_error
25
+ end
26
+ end
27
+ end
28
+
29
+ context "without block" do
30
+ it "fires the event" do
31
+ expect(handler).to receive(:event).with(:name, :arg)
32
+ subject.event(:name, :arg)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ module Itamae
4
+ describe Handler do
5
+ describe ".from_type" do
6
+ it "returns handler class" do
7
+ expect(described_class.from_type('debug')).to eq(Handler::Debug)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ module Itamae
4
+ describe Node do
5
+ let(:backend) { nil }
6
+ describe "#reverse_merge" do
7
+ it "merges a hash but the method receiver's value will be preferred" do
8
+ a = described_class.new({a: :b, c: :d}, backend)
9
+ expected = described_class.new({a: :b, c: :d, e: :f}, backend)
10
+ expect(a.reverse_merge(a: :c, e: :f).mash).to eq(expected.mash)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ module Itamae
4
+ describe Recipe do
5
+ end
6
+ end
@@ -0,0 +1,127 @@
1
+ require 'itamae'
2
+
3
+ class DefineAttributeTestResource < Itamae::Resource::Base
4
+ define_attribute :action, default: :create
5
+ define_attribute :default_attribute, default: :something
6
+ define_attribute :required_attribute, required: true
7
+ define_attribute :typed_attribute, type: Numeric
8
+ define_attribute :default_name_attribute, default_name: true
9
+ end
10
+
11
+ describe DefineAttributeTestResource do
12
+ describe "define_attribute" do
13
+ describe "default" do
14
+ subject do
15
+ described_class.new(double(:recipe), 'resource name') do
16
+ required_attribute :required_value
17
+ end
18
+ end
19
+ it "returns the default value" do
20
+ expect(subject.attributes[:default_attribute]).to eq(:something)
21
+ end
22
+ end
23
+
24
+ describe "falsey" do
25
+ subject do
26
+ described_class.new(double(:recipe), 'resource name') do
27
+ required_attribute :required_value
28
+ default_attribute nil
29
+ end
30
+ end
31
+ it "returns the default value" do
32
+ expect(subject.attributes[:default_attribute]).to eq(nil)
33
+ end
34
+ end
35
+
36
+ describe "required" do
37
+ subject do
38
+ described_class.new(double(:recipe), 'resource name') do
39
+ #required_attribute :required_value
40
+ end
41
+ end
42
+ context "without setting required attribute" do
43
+ it "raises an error" do
44
+ expect do
45
+ subject
46
+ end.to raise_error(Itamae::Resource::AttributeMissingError)
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "type" do
52
+ context "with correct type value" do
53
+ subject do
54
+ described_class.new(double(:recipe), 'resource name') do
55
+ required_attribute :required_value
56
+ typed_attribute 10
57
+ end
58
+ end
59
+ it "returns the value" do
60
+ expect(subject.attributes[:typed_attribute]).to eq(10)
61
+ end
62
+ end
63
+
64
+ context "with incorrect type value" do
65
+ subject do
66
+ described_class.new(double(:recipe), 'resource name') do
67
+ required_attribute :required_value
68
+ typed_attribute "string"
69
+ end
70
+ end
71
+ it "raises an error" do
72
+ expect do
73
+ subject
74
+ end.to raise_error(Itamae::Resource::InvalidTypeError)
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "default_name" do
80
+ context "without setting the value" do
81
+ subject do
82
+ described_class.new(double(:recipe), 'resource name') do
83
+ required_attribute :required_value
84
+ end
85
+ end
86
+ it "returns the resource name" do
87
+ expect(subject.attributes[:default_name_attribute]).
88
+ to eq("resource name")
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ class TestResource < Itamae::Resource::Base
96
+ define_attribute :action, default: :create
97
+ define_attribute :attribute_key, required: false
98
+ end
99
+
100
+ describe TestResource do
101
+ subject(:resource) { described_class.new(recipe, "name") }
102
+
103
+ let(:handler) { Itamae::HandlerProxy.new }
104
+ let(:runner) do
105
+ instance_double(Itamae::Runner).tap do |r|
106
+ allow(r).to receive(:dry_run?).and_return(false)
107
+ allow(r).to receive(:handler).and_return(handler)
108
+ end
109
+ end
110
+ let(:recipe) do
111
+ instance_double(Itamae::Recipe).tap do |r|
112
+ allow(r).to receive(:runner).and_return(runner)
113
+ end
114
+ end
115
+
116
+ describe "#run" do
117
+ before do
118
+ subject.attributes.action = :name
119
+ allow(Itamae::Logger).to receive(:debug) # suppress logger output
120
+ end
121
+
122
+ it "executes <ACTION_NAME>_action method" do
123
+ expect(subject).to receive(:action_name)
124
+ subject.run
125
+ end
126
+ end
127
+ end