itamae-spec 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +42 -0
- data/Rakefile +47 -0
- data/bin/itamae-spec +4 -0
- data/itamae-spec.gemspec +36 -0
- data/lib/itamae-spec.rb +10 -0
- data/lib/itamae-spec/cli.rb +19 -0
- data/lib/itamae-spec/generators.rb +20 -0
- data/lib/itamae-spec/generators/cookbook.rb +10 -0
- data/lib/itamae-spec/generators/project.rb +10 -0
- data/lib/itamae-spec/generators/templates/cookbook/attributes/.keep +0 -0
- data/lib/itamae-spec/generators/templates/cookbook/recipes/default.rb +0 -0
- data/lib/itamae-spec/generators/templates/cookbook/recipes/files/.keep +0 -0
- data/lib/itamae-spec/generators/templates/cookbook/recipes/templates/.keep +0 -0
- data/lib/itamae-spec/generators/templates/cookbook/spec/default_spec.rb +0 -0
- data/lib/itamae-spec/generators/templates/project/.rspec +2 -0
- data/lib/itamae-spec/generators/templates/project/Gemfile +3 -0
- data/lib/itamae-spec/generators/templates/project/Project.json +1 -0
- data/lib/itamae-spec/generators/templates/project/Rakefile +9 -0
- data/lib/itamae-spec/generators/templates/project/cookbooks/sample/attributes/.keep +0 -0
- data/lib/itamae-spec/generators/templates/project/cookbooks/sample/attributes/default.json +5 -0
- data/lib/itamae-spec/generators/templates/project/cookbooks/sample/recipes/default.rb +7 -0
- data/lib/itamae-spec/generators/templates/project/cookbooks/sample/recipes/files/.keep +0 -0
- data/lib/itamae-spec/generators/templates/project/cookbooks/sample/recipes/templates/.keep +0 -0
- data/lib/itamae-spec/generators/templates/project/cookbooks/sample/spec/default_spec.rb +9 -0
- data/lib/itamae-spec/generators/templates/project/environments/.keep +0 -0
- data/lib/itamae-spec/generators/templates/project/environments/sample.json +7 -0
- data/lib/itamae-spec/generators/templates/project/keys/.keep +0 -0
- data/lib/itamae-spec/generators/templates/project/nodes/.keep +0 -0
- data/lib/itamae-spec/generators/templates/project/nodes/sample.json +10 -0
- data/lib/itamae-spec/generators/templates/project/roles/.keep +0 -0
- data/lib/itamae-spec/generators/templates/project/roles/sample.json +5 -0
- data/lib/itamae-spec/generators/templates/project/spec/spec_helper.rb +41 -0
- data/lib/itamae-spec/generators/templates/project/tmp-nodes/.keep +0 -0
- data/lib/itamae-spec/logger.rb +76 -0
- data/lib/itamae-spec/resource.rb +2 -0
- data/lib/itamae-spec/resource/http_request.rb +71 -0
- data/lib/itamae-spec/resource/s3_file.rb +31 -0
- data/lib/itamae-spec/task/base.rb +90 -0
- data/lib/itamae-spec/task/base_task.rb +148 -0
- data/lib/itamae-spec/task/itamae_task.rb +112 -0
- data/lib/itamae-spec/task/local_itamae_task.rb +84 -0
- data/lib/itamae-spec/task/local_serverspec_task.rb +125 -0
- data/lib/itamae-spec/task/serverspec_task.rb +111 -0
- data/lib/itamae-spec/version.rb +3 -0
- data/lib/itamae-spec/version.txt +1 -0
- data/spec/integration/Vagrantfile +35 -0
- data/spec/integration/default_spec.rb +226 -0
- data/spec/integration/recipes/default.rb +423 -0
- data/spec/integration/recipes/default2.rb +6 -0
- data/spec/integration/recipes/define/default.rb +6 -0
- data/spec/integration/recipes/define/files/remote_file_in_definition +1 -0
- data/spec/integration/recipes/dry_run.rb +6 -0
- data/spec/integration/recipes/files/remote_file_auto +1 -0
- data/spec/integration/recipes/hello.erb +6 -0
- data/spec/integration/recipes/hello.txt +1 -0
- data/spec/integration/recipes/included.rb +9 -0
- data/spec/integration/recipes/node.json +3 -0
- data/spec/integration/recipes/redefine.rb +20 -0
- data/spec/integration/recipes/templates/template_auto.erb +6 -0
- data/spec/integration/spec_helper.rb +42 -0
- data/spec/unit/lib/itamae/backend_spec.rb +95 -0
- data/spec/unit/lib/itamae/handler/base_spec.rb +34 -0
- data/spec/unit/lib/itamae/handler/fluentd_spec.rb +19 -0
- data/spec/unit/lib/itamae/handler_proxy_spec.rb +38 -0
- data/spec/unit/lib/itamae/handler_spec.rb +11 -0
- data/spec/unit/lib/itamae/node_spec.rb +14 -0
- data/spec/unit/lib/itamae/recipe_spec.rb +6 -0
- data/spec/unit/lib/itamae/resource/base_spec.rb +127 -0
- data/spec/unit/lib/itamae/resource_spec.rb +23 -0
- data/spec/unit/lib/itamae/runner_spec.rb +32 -0
- data/spec/unit/spec_helper.rb +23 -0
- metadata +315 -0
@@ -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,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,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,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,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
|