itamae 1.0.0.beta1 → 1.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -8
- data/Rakefile +1 -0
- data/itamae.gemspec +2 -1
- data/lib/itamae.rb +2 -1
- data/lib/itamae/cli.rb +8 -0
- data/lib/itamae/logger.rb +15 -2
- data/lib/itamae/recipe.rb +11 -11
- data/lib/itamae/resource.rb +37 -0
- data/lib/itamae/resource/base.rb +257 -0
- data/lib/itamae/resource/directory.rb +35 -0
- data/lib/itamae/resource/execute.rb +16 -0
- data/lib/itamae/{resources → resource}/file.rb +8 -8
- data/lib/itamae/resource/mail_alias.rb +17 -0
- data/lib/itamae/{resources → resource}/package.rb +3 -3
- data/lib/itamae/{resources → resource}/remote_file.rb +2 -2
- data/lib/itamae/{resources → resource}/template.rb +2 -2
- data/lib/itamae/resource_collection.rb +27 -0
- data/lib/itamae/specinfra.rb +1 -1
- data/lib/itamae/version.rb +1 -1
- data/spec/integration/default_spec.rb +22 -0
- data/spec/integration/recipes/default.rb +49 -0
- data/spec/unit/lib/itamae/{resources → resource}/base_spec.rb +27 -27
- data/spec/unit/lib/itamae/{resources → resource}/package_spec.rb +1 -1
- data/spec/unit/lib/itamae/{resources → resource}/remote_file_spec.rb +1 -1
- data/spec/unit/lib/itamae/resource_spec.rb +31 -0
- metadata +36 -19
- data/lib/itamae/resources.rb +0 -25
- data/lib/itamae/resources/base.rb +0 -151
- data/lib/itamae/resources/directory.rb +0 -26
- data/spec/unit/lib/itamae/resources_spec.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 240aa7e6557872fd35c36c24692c8a0e3f714809
|
4
|
+
data.tar.gz: 516ef413cfc3a830f63fb83b9d1207fff66a5f00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 769d71f69c3452ebfbfae501b1278a1931ca5306ce00f2c88a681586e1da78ecc83f723b0f04616c4216c2901158dde1f5e98fb62c8f52e5c299699eb99abc8b
|
7
|
+
data.tar.gz: 16cde3481cf61c3d8aa60adaa3e630e293f36263674dea4dda08cde2e83cc54bf048746501fc0de098dbc6b7ec84ee45dda63da65653195dbd6414392b9dc46c
|
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
# Itamae
|
1
|
+
# Itamae [![Gem Version](https://badge.fury.io/rb/itamae.svg)](http://badge.fury.io/rb/itamae)
|
2
2
|
|
3
|
-
|
3
|
+
Simple and lightweight configuration management tool inspired by Chef.
|
4
4
|
|
5
5
|
## Concept
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
- Chef-like DSL
|
8
|
+
- Simpler and lighter weight than Chef
|
9
|
+
- Not compatible with Chef
|
10
|
+
- Idempotent
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
@@ -22,7 +22,7 @@ $ gem install itamae
|
|
22
22
|
```
|
23
23
|
$ sudo itamae execute -j example/node.json example/recipe.rb
|
24
24
|
D, [2013-12-24T14:05:50.859587 #7156] DEBUG -- : Loading node data from /vagrant/example/node.json ...
|
25
|
-
I, [2013-12-24T14:05:50.862072 #7156] INFO -- : >>> Executing Itamae::
|
25
|
+
I, [2013-12-24T14:05:50.862072 #7156] INFO -- : >>> Executing Itamae::Resource::Package ({:action=>:install, :name=>"git"})...
|
26
26
|
D, [2013-12-24T14:05:51.335070 #7156] DEBUG -- : Command `apt-get -y install git` succeeded
|
27
27
|
D, [2013-12-24T14:05:51.335251 #7156] DEBUG -- : STDOUT> Reading package lists...
|
28
28
|
Building dependency tree...
|
@@ -31,7 +31,7 @@ git is already the newest version.
|
|
31
31
|
0 upgraded, 0 newly installed, 0 to remove and 156 not upgraded.
|
32
32
|
D, [2013-12-24T14:05:51.335464 #7156] DEBUG -- : STDERR>
|
33
33
|
I, [2013-12-24T14:05:51.335531 #7156] INFO -- : <<< Succeeded.
|
34
|
-
I, [2013-12-24T14:05:51.335728 #7156] INFO -- : >>> Executing Itamae::
|
34
|
+
I, [2013-12-24T14:05:51.335728 #7156] INFO -- : >>> Executing Itamae::Resource::File ({:action=>:create, :source=>"foo", :path=>"/home/vagrant/foo"})...
|
35
35
|
D, [2013-12-24T14:05:51.335842 #7156] DEBUG -- : Copying a file from '/vagrant/example/foo' to '/home/vagrant/foo'...
|
36
36
|
I, [2013-12-24T14:05:51.339119 #7156] INFO -- : <<< Succeeded.
|
37
37
|
```
|
data/Rakefile
CHANGED
@@ -40,6 +40,7 @@ namespace :spec do
|
|
40
40
|
cmd << " -u #{options[:user]}"
|
41
41
|
cmd << " -p #{options[:port]}"
|
42
42
|
cmd << " -i #{options[:keys].first}"
|
43
|
+
cmd << " -l #{ENV['LOG_LEVEL'] || 'debug'}"
|
43
44
|
cmd << " -j spec/integration/recipes/node.json"
|
44
45
|
cmd << " spec/integration/recipes/default.rb"
|
45
46
|
|
data/itamae.gemspec
CHANGED
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
20
|
spec.add_runtime_dependency "thor"
|
21
|
-
spec.add_runtime_dependency "specinfra", "2.0.0.
|
21
|
+
spec.add_runtime_dependency "specinfra", "2.0.0.beta32"
|
22
22
|
spec.add_runtime_dependency "hashie"
|
23
|
+
spec.add_runtime_dependency "ansi"
|
23
24
|
|
24
25
|
# TODO: move to specinfra
|
25
26
|
spec.add_runtime_dependency "net-scp"
|
data/lib/itamae.rb
CHANGED
@@ -2,7 +2,8 @@ require "itamae/version"
|
|
2
2
|
require "itamae/runner"
|
3
3
|
require "itamae/cli"
|
4
4
|
require "itamae/recipe"
|
5
|
-
require "itamae/
|
5
|
+
require "itamae/resource"
|
6
|
+
require "itamae/resource_collection"
|
6
7
|
require "itamae/logger"
|
7
8
|
require "itamae/node"
|
8
9
|
require "itamae/specinfra"
|
data/lib/itamae/cli.rb
CHANGED
@@ -3,6 +3,14 @@ require 'thor'
|
|
3
3
|
|
4
4
|
module Itamae
|
5
5
|
class CLI < Thor
|
6
|
+
class_option :log_level, type: :string, aliases: ['-l'], default: 'info'
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
|
11
|
+
Itamae::Logger.level = ::Logger.const_get(options[:log_level].upcase)
|
12
|
+
end
|
13
|
+
|
6
14
|
desc "local RECIPE [RECIPE...]", "Run Itamae locally"
|
7
15
|
option :node_json, type: :string, aliases: ['-j']
|
8
16
|
def local(*recipe_files)
|
data/lib/itamae/logger.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
require 'itamae'
|
2
2
|
require 'logger'
|
3
|
+
require 'ansi/code'
|
3
4
|
|
4
5
|
module Itamae
|
5
6
|
module Logger
|
6
7
|
class Formatter
|
7
8
|
def call(severity, datetime, progname, msg)
|
8
|
-
"[%s] %
|
9
|
+
"[%s] %s : %s\n" % [format_datetime(datetime), color("%5s" % severity, severity), msg2str(msg)]
|
9
10
|
end
|
10
11
|
|
11
12
|
private
|
12
13
|
def format_datetime(time)
|
13
|
-
time.strftime("%Y-%m-%dT%H:%M:%S
|
14
|
+
time.strftime("%Y-%m-%dT%H:%M:%S%:z")
|
14
15
|
end
|
15
16
|
|
16
17
|
def msg2str(msg)
|
@@ -24,6 +25,18 @@ module Itamae
|
|
24
25
|
msg.inspect
|
25
26
|
end
|
26
27
|
end
|
28
|
+
|
29
|
+
def color(str, severity)
|
30
|
+
color_code = case severity
|
31
|
+
when "INFO"
|
32
|
+
:green
|
33
|
+
when "ERROR"
|
34
|
+
:red
|
35
|
+
else
|
36
|
+
:clear
|
37
|
+
end
|
38
|
+
ANSI.public_send(color_code) { str }
|
39
|
+
end
|
27
40
|
end
|
28
41
|
|
29
42
|
def self.logger
|
data/lib/itamae/recipe.rb
CHANGED
@@ -4,11 +4,15 @@ module Itamae
|
|
4
4
|
class Recipe
|
5
5
|
attr_reader :path
|
6
6
|
attr_reader :runner
|
7
|
+
attr_reader :resources
|
8
|
+
attr_reader :delayed_actions
|
7
9
|
|
8
10
|
def initialize(runner, path)
|
9
11
|
@runner = runner
|
10
12
|
@path = path
|
11
|
-
@resources =
|
13
|
+
@resources = ResourceCollection.new
|
14
|
+
@delayed_actions = []
|
15
|
+
|
12
16
|
load_resources
|
13
17
|
end
|
14
18
|
|
@@ -18,15 +22,11 @@ module Itamae
|
|
18
22
|
|
19
23
|
def run
|
20
24
|
@resources.each do |resource|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
exit 2
|
27
|
-
else
|
28
|
-
Logger.info "<<< Succeeded."
|
29
|
-
end
|
25
|
+
resource.run
|
26
|
+
end
|
27
|
+
|
28
|
+
@delayed_actions.uniq.each do |action, resource|
|
29
|
+
resource.run(action)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -37,7 +37,7 @@ module Itamae
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def method_missing(method, name, &block)
|
40
|
-
klass =
|
40
|
+
klass = Resource.get_resource_class(method)
|
41
41
|
resource = klass.new(self, name, &block)
|
42
42
|
@resources << resource
|
43
43
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
require 'itamae/resource/base'
|
3
|
+
require 'itamae/resource/file'
|
4
|
+
require 'itamae/resource/package'
|
5
|
+
require 'itamae/resource/remote_file'
|
6
|
+
require 'itamae/resource/directory'
|
7
|
+
require 'itamae/resource/template'
|
8
|
+
require 'itamae/resource/execute'
|
9
|
+
require 'itamae/resource/mail_alias'
|
10
|
+
|
11
|
+
module Itamae
|
12
|
+
module Resource
|
13
|
+
Error = Class.new(StandardError)
|
14
|
+
CommandExecutionError = Class.new(StandardError)
|
15
|
+
AttributeMissingError = Class.new(StandardError)
|
16
|
+
InvalidTypeError = Class.new(StandardError)
|
17
|
+
ParseError = Class.new(StandardError)
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def get_resource_class_name(method)
|
21
|
+
method.to_s.split('_').map {|part| part.capitalize}.join
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_resource_class(method)
|
25
|
+
const_get(get_resource_class_name(method))
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_description(desc)
|
29
|
+
if /\A([^\[]+)\[([^\]]+)\]\z/ =~ desc
|
30
|
+
[$1, $2]
|
31
|
+
else
|
32
|
+
raise ParseError, "'#{desc}' doesn't represent a resource."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module Itamae
|
5
|
+
module Resource
|
6
|
+
class Base
|
7
|
+
@defined_attributes ||= {}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :defined_attributes
|
11
|
+
attr_reader :supported_oses
|
12
|
+
|
13
|
+
def inherited(subclass)
|
14
|
+
subclass.instance_variable_set(
|
15
|
+
:@defined_attributes,
|
16
|
+
self.defined_attributes.dup
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_attribute(name, options)
|
21
|
+
current = @defined_attributes[name.to_sym] || {}
|
22
|
+
@defined_attributes[name.to_sym] = current.merge(options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
define_attribute :action, type: Symbol, required: true
|
27
|
+
|
28
|
+
attr_reader :resource_name
|
29
|
+
attr_reader :attributes
|
30
|
+
attr_reader :current_attributes
|
31
|
+
|
32
|
+
def initialize(recipe, resource_name, &block)
|
33
|
+
@attributes = {}
|
34
|
+
@current_attributes = {}
|
35
|
+
@recipe = recipe
|
36
|
+
@resource_name = resource_name
|
37
|
+
@notifies = []
|
38
|
+
@subscribes = []
|
39
|
+
@updated = false
|
40
|
+
|
41
|
+
instance_eval(&block) if block_given?
|
42
|
+
|
43
|
+
process_attributes
|
44
|
+
end
|
45
|
+
|
46
|
+
def run(specific_action = nil)
|
47
|
+
Logger.info "> Executing #{resource_type} (#{attributes})..."
|
48
|
+
|
49
|
+
if do_not_run_because_of_only_if?
|
50
|
+
Logger.info "< Execution skipped because of only_if attribute"
|
51
|
+
return
|
52
|
+
elsif do_not_run_because_of_not_if?
|
53
|
+
Logger.info "< Execution skipped because of not_if attribute"
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
set_current_attributes
|
58
|
+
show_differences
|
59
|
+
|
60
|
+
begin
|
61
|
+
public_send("#{specific_action || action}_action".to_sym)
|
62
|
+
rescue Resource::CommandExecutionError
|
63
|
+
Logger.error "< Failed."
|
64
|
+
exit 2
|
65
|
+
end
|
66
|
+
|
67
|
+
notify if updated?
|
68
|
+
|
69
|
+
Logger.info "< Succeeded."
|
70
|
+
end
|
71
|
+
|
72
|
+
def nothing_action
|
73
|
+
# do nothing
|
74
|
+
end
|
75
|
+
|
76
|
+
def resource_type
|
77
|
+
humps = []
|
78
|
+
self.class.name.split("::").last.each_char do |c|
|
79
|
+
if "A" <= c && c <= "Z"
|
80
|
+
humps << c.downcase
|
81
|
+
else
|
82
|
+
humps.last << c
|
83
|
+
end
|
84
|
+
end
|
85
|
+
humps.join('_')
|
86
|
+
end
|
87
|
+
|
88
|
+
def notifies_resources
|
89
|
+
@notifies.map do |action, resource_desc, timing|
|
90
|
+
resource = resources.find_by_description(resource_desc)
|
91
|
+
[action, resource, timing]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def subscribes_resources
|
96
|
+
@subscribes.map do |action, resource_desc, timing|
|
97
|
+
resource = resources.find_by_description(resource_desc)
|
98
|
+
[action, resource, timing]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def method_missing(method, *args)
|
106
|
+
if args.size == 1 && self.class.defined_attributes[method]
|
107
|
+
return @attributes[method] = args.first
|
108
|
+
elsif args.size == 0 && @attributes.has_key?(method)
|
109
|
+
return @attributes[method]
|
110
|
+
end
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
def set_current_attributes
|
115
|
+
end
|
116
|
+
|
117
|
+
def show_differences
|
118
|
+
@current_attributes.each_pair do |key, current_value|
|
119
|
+
value = @attributes[key]
|
120
|
+
Logger.info " #{key} will change from '#{current_value}' to '#{value}'"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def process_attributes
|
125
|
+
self.class.defined_attributes.each_pair do |key, details|
|
126
|
+
@attributes[key] ||= @resource_name if details[:default_name]
|
127
|
+
@attributes[key] ||= details[:default]
|
128
|
+
|
129
|
+
if details[:required] && !@attributes[key]
|
130
|
+
raise Resource::AttributeMissingError, "'#{key}' attribute is required but it is not set."
|
131
|
+
end
|
132
|
+
|
133
|
+
if @attributes[key] && details[:type] && !@attributes[key].is_a?(details[:type])
|
134
|
+
raise Resource::InvalidTypeError, "#{key} attribute should be #{details[:type]}."
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def run_specinfra(type, *args)
|
140
|
+
command = Specinfra.command.get(type, *args)
|
141
|
+
|
142
|
+
if type.to_s.start_with?("check_")
|
143
|
+
result = run_command(command, error: false)
|
144
|
+
result.exit_status == 0
|
145
|
+
else
|
146
|
+
run_command(command)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def run_command(command, options = {})
|
151
|
+
options = {error: true}.merge(options)
|
152
|
+
|
153
|
+
result = backend.run_command(command)
|
154
|
+
exit_status = result.exit_status
|
155
|
+
|
156
|
+
if exit_status == 0 || !options[:error]
|
157
|
+
method = :debug
|
158
|
+
message = " Command `#{command}` exited with #{exit_status}"
|
159
|
+
else
|
160
|
+
method = :error
|
161
|
+
message = " Command `#{command}` failed. (exit status: #{exit_status})"
|
162
|
+
end
|
163
|
+
|
164
|
+
Logger.public_send(method, message)
|
165
|
+
|
166
|
+
if result.stdout && result.stdout != ''
|
167
|
+
Logger.public_send(method, " STDOUT> #{result.stdout.chomp}")
|
168
|
+
end
|
169
|
+
|
170
|
+
if result.stderr && result.stderr != ''
|
171
|
+
Logger.public_send(method, " STDERR> #{result.stderr.chomp}")
|
172
|
+
end
|
173
|
+
|
174
|
+
if options[:error] && exit_status != 0
|
175
|
+
raise CommandExecutionError
|
176
|
+
end
|
177
|
+
|
178
|
+
result
|
179
|
+
end
|
180
|
+
|
181
|
+
def copy_file(src, dst)
|
182
|
+
Logger.debug " Copying a file from '#{src}' to '#{dst}'..."
|
183
|
+
unless ::File.exist?(src)
|
184
|
+
raise Error, "The file '#{src}' doesn't exist."
|
185
|
+
end
|
186
|
+
backend.copy_file(src, dst)
|
187
|
+
end
|
188
|
+
|
189
|
+
def only_if(command)
|
190
|
+
@only_if_command = command
|
191
|
+
end
|
192
|
+
|
193
|
+
def not_if(command)
|
194
|
+
@not_if_command = command
|
195
|
+
end
|
196
|
+
|
197
|
+
def do_not_run_because_of_only_if?
|
198
|
+
@only_if_command &&
|
199
|
+
run_command(@only_if_command, error: false).exit_status != 0
|
200
|
+
end
|
201
|
+
|
202
|
+
def do_not_run_because_of_not_if?
|
203
|
+
@not_if_command &&
|
204
|
+
run_command(@not_if_command, error: false).exit_status == 0
|
205
|
+
end
|
206
|
+
|
207
|
+
def notifies(action, resource_desc, timing = :delay)
|
208
|
+
@notifies << [action, resource_desc, timing]
|
209
|
+
end
|
210
|
+
|
211
|
+
def subscribes(action, resource_desc, timing = :delay)
|
212
|
+
@subscribes << [action, resource_desc, timing]
|
213
|
+
end
|
214
|
+
|
215
|
+
def node
|
216
|
+
runner.node
|
217
|
+
end
|
218
|
+
|
219
|
+
def backend
|
220
|
+
Itamae.backend
|
221
|
+
end
|
222
|
+
|
223
|
+
def runner
|
224
|
+
@recipe.runner
|
225
|
+
end
|
226
|
+
|
227
|
+
def resources
|
228
|
+
@recipe.resources
|
229
|
+
end
|
230
|
+
|
231
|
+
def shell_escape(str)
|
232
|
+
Shellwords.escape(str)
|
233
|
+
end
|
234
|
+
|
235
|
+
def updated!
|
236
|
+
@updated = true
|
237
|
+
end
|
238
|
+
|
239
|
+
def updated?
|
240
|
+
@updated
|
241
|
+
end
|
242
|
+
|
243
|
+
def notify
|
244
|
+
action_resource_timing = notifies_resources + resources.subscribing(self)
|
245
|
+
action_resource_timing.uniq.each do |action, resource, timing|
|
246
|
+
case timing
|
247
|
+
when :immediately
|
248
|
+
resource.run(action)
|
249
|
+
when :delay
|
250
|
+
@recipe.delayed_actions << [action, resource]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
|
3
|
+
module Itamae
|
4
|
+
module Resource
|
5
|
+
class Directory < Base
|
6
|
+
define_attribute :action, default: :create
|
7
|
+
define_attribute :path, type: String, default_name: true
|
8
|
+
define_attribute :mode, type: String
|
9
|
+
define_attribute :owner, type: String
|
10
|
+
define_attribute :group, type: String
|
11
|
+
|
12
|
+
def set_current_attributes
|
13
|
+
escaped_path = shell_escape(path)
|
14
|
+
if run_command("test -d #{escaped_path}", error: false).exit_status == 0
|
15
|
+
@current_attributes[:mode] = run_command("stat --format '%a' #{escaped_path}").stdout.chomp
|
16
|
+
@current_attributes[:owner] = run_command("stat --format '%U' #{escaped_path}").stdout.chomp
|
17
|
+
@current_attributes[:group] = run_command("stat --format '%G' #{escaped_path}").stdout.chomp
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_action
|
22
|
+
if ! run_specinfra(:check_file_is_directory, path)
|
23
|
+
run_specinfra(:create_file_as_directory, path)
|
24
|
+
end
|
25
|
+
if attributes[:mode]
|
26
|
+
run_specinfra(:change_file_mode, path, attributes[:mode])
|
27
|
+
end
|
28
|
+
if attributes[:owner] || attributes[:group]
|
29
|
+
run_specinfra(:change_file_owner, path, attributes[:owner], attributes[:group])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
|
3
|
+
module Itamae
|
4
|
+
module Resource
|
5
|
+
class Execute < Base
|
6
|
+
define_attribute :action, default: :run
|
7
|
+
define_attribute :command, type: String, default_name: true
|
8
|
+
|
9
|
+
def run_action
|
10
|
+
run_command(command)
|
11
|
+
updated!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -1,15 +1,15 @@
|
|
1
1
|
require 'itamae'
|
2
2
|
|
3
3
|
module Itamae
|
4
|
-
module
|
4
|
+
module Resource
|
5
5
|
class File < Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
define_attribute :action, default: :create
|
7
|
+
define_attribute :path, type: String, default_name: true
|
8
|
+
define_attribute :content, type: String, default: ''
|
9
|
+
define_attribute :content_file, type: String
|
10
|
+
define_attribute :mode, type: String
|
11
|
+
define_attribute :owner, type: String
|
12
|
+
define_attribute :group, type: String
|
13
13
|
|
14
14
|
def create_action
|
15
15
|
src = if content_file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
|
3
|
+
module Itamae
|
4
|
+
module Resource
|
5
|
+
class MailAlias < Base
|
6
|
+
define_attribute :action, default: :create
|
7
|
+
define_attribute :mail_alias, type: String, default_name: true
|
8
|
+
define_attribute :recipient, type: String, required: true
|
9
|
+
|
10
|
+
def create_action
|
11
|
+
if ! backend.check_mail_alias_is_aliased_to(mail_alias, recipient)
|
12
|
+
backend.add_mail_alias(mail_alias, recipient)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'itamae'
|
2
2
|
|
3
3
|
module Itamae
|
4
|
-
module
|
4
|
+
module Resource
|
5
5
|
class Package < Base
|
6
|
-
|
7
|
-
|
6
|
+
define_attribute :action, default: :install
|
7
|
+
define_attribute :name, type: String, default_name: true
|
8
8
|
|
9
9
|
def install_action
|
10
10
|
run_specinfra(:install_package, name)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'itamae'
|
2
2
|
|
3
3
|
module Itamae
|
4
|
-
module
|
4
|
+
module Resource
|
5
5
|
class RemoteFile < File
|
6
|
-
|
6
|
+
define_attribute :source, type: String, required: true
|
7
7
|
|
8
8
|
def create_action
|
9
9
|
content_file(::File.expand_path(source, ::File.dirname(@recipe.path)))
|
@@ -3,9 +3,9 @@ require 'erb'
|
|
3
3
|
require 'tempfile'
|
4
4
|
|
5
5
|
module Itamae
|
6
|
-
module
|
6
|
+
module Resource
|
7
7
|
class Template < File
|
8
|
-
|
8
|
+
define_attribute :source, type: String, required: true
|
9
9
|
|
10
10
|
def create_action
|
11
11
|
src = ::File.expand_path(source, ::File.dirname(@recipe.path))
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Itamae
|
2
|
+
class ResourceCollection < Array
|
3
|
+
NotFoundError = Class.new(StandardError)
|
4
|
+
|
5
|
+
def find_by_description(desc)
|
6
|
+
# desc is like 'resource_type[name]'
|
7
|
+
self.find do |resource|
|
8
|
+
type, name = Itamae::Resource.parse_description(desc)
|
9
|
+
resource.resource_type == type && resource.resource_name == name
|
10
|
+
end.tap do |resource|
|
11
|
+
unless resource
|
12
|
+
raise NotFoundError, "'#{desc}' resource is not found."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def subscribing(target)
|
18
|
+
self.map do |resource|
|
19
|
+
resource.subscribes_resources.map do |action, r, timing|
|
20
|
+
if r == target
|
21
|
+
[action, resource, timing]
|
22
|
+
end
|
23
|
+
end.compact
|
24
|
+
end.flatten(1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/itamae/specinfra.rb
CHANGED
data/lib/itamae/version.rb
CHANGED
@@ -30,4 +30,26 @@ describe file('/tmp/file') do
|
|
30
30
|
its(:content) { should match(/Hello World/) }
|
31
31
|
end
|
32
32
|
|
33
|
+
describe file('/tmp/execute') do
|
34
|
+
it { should be_file }
|
35
|
+
its(:content) { should match(/Hello Execute/) }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe file('/tmp/never_exist1') do
|
39
|
+
it { should_not be_file }
|
40
|
+
end
|
41
|
+
|
42
|
+
describe file('/tmp/never_exist2') do
|
43
|
+
it { should_not be_file }
|
44
|
+
end
|
45
|
+
|
46
|
+
describe file('/tmp/notifies') do
|
47
|
+
it { should be_file }
|
48
|
+
its(:content) { should eq("2431") }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe file('/tmp/subscribes') do
|
52
|
+
it { should be_file }
|
53
|
+
its(:content) { should eq("2431") }
|
54
|
+
end
|
33
55
|
|
@@ -4,6 +4,46 @@ end
|
|
4
4
|
|
5
5
|
package 'sl'
|
6
6
|
|
7
|
+
######
|
8
|
+
|
9
|
+
execute "echo -n > /tmp/notifies"
|
10
|
+
|
11
|
+
execute "echo -n 1 >> /tmp/notifies" do
|
12
|
+
action :nothing
|
13
|
+
end
|
14
|
+
|
15
|
+
execute "echo -n 2 >> /tmp/notifies" do
|
16
|
+
notifies :run, "execute[echo -n 1 >> /tmp/notifies]"
|
17
|
+
end
|
18
|
+
|
19
|
+
execute "echo -n 3 >> /tmp/notifies" do
|
20
|
+
action :nothing
|
21
|
+
end
|
22
|
+
|
23
|
+
execute "echo -n 4 >> /tmp/notifies" do
|
24
|
+
notifies :run, "execute[echo -n 3 >> /tmp/notifies]", :immediately
|
25
|
+
end
|
26
|
+
|
27
|
+
######
|
28
|
+
|
29
|
+
execute "echo -n > /tmp/subscribes"
|
30
|
+
|
31
|
+
execute "echo -n 1 >> /tmp/subscribes" do
|
32
|
+
action :nothing
|
33
|
+
subscribes :run, "execute[echo -n 2 >> /tmp/subscribes]"
|
34
|
+
end
|
35
|
+
|
36
|
+
execute "echo -n 2 >> /tmp/subscribes"
|
37
|
+
|
38
|
+
execute "echo -n 3 >> /tmp/subscribes" do
|
39
|
+
action :nothing
|
40
|
+
subscribes :run, "execute[echo -n 4 >> /tmp/subscribes]", :immediately
|
41
|
+
end
|
42
|
+
|
43
|
+
execute "echo -n 4 >> /tmp/subscribes"
|
44
|
+
|
45
|
+
######
|
46
|
+
|
7
47
|
remote_file "/tmp/remote_file" do
|
8
48
|
source "hello.txt"
|
9
49
|
end
|
@@ -22,5 +62,14 @@ file "/tmp/file" do
|
|
22
62
|
content "Hello World"
|
23
63
|
end
|
24
64
|
|
65
|
+
execute "echo 'Hello Execute' > /tmp/execute"
|
66
|
+
|
67
|
+
file "/tmp/never_exist1" do
|
68
|
+
only_if "exit 1"
|
69
|
+
end
|
70
|
+
|
71
|
+
file "/tmp/never_exist2" do
|
72
|
+
not_if "exit 0"
|
73
|
+
end
|
25
74
|
|
26
75
|
|
@@ -1,37 +1,37 @@
|
|
1
1
|
require 'itamae'
|
2
2
|
|
3
|
-
class
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
9
|
end
|
10
10
|
|
11
|
-
describe
|
12
|
-
describe "
|
11
|
+
describe DefineAttributeTestResource do
|
12
|
+
describe "define_attribute" do
|
13
13
|
describe "default" do
|
14
14
|
subject do
|
15
15
|
described_class.new(double(:recipe), 'resource name') do
|
16
|
-
|
16
|
+
required_attribute :required_value
|
17
17
|
end
|
18
18
|
end
|
19
19
|
it "returns the default value" do
|
20
|
-
expect(subject.
|
20
|
+
expect(subject.attributes[:default_attribute]).to eq(:something)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
describe "required" do
|
25
25
|
subject do
|
26
26
|
described_class.new(double(:recipe), 'resource name') do
|
27
|
-
#
|
27
|
+
#required_attribute :required_value
|
28
28
|
end
|
29
29
|
end
|
30
|
-
context "without setting required
|
30
|
+
context "without setting required attribute" do
|
31
31
|
it "raises an error" do
|
32
32
|
expect do
|
33
33
|
subject
|
34
|
-
end.to raise_error(Itamae::
|
34
|
+
end.to raise_error(Itamae::Resource::AttributeMissingError)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -40,26 +40,26 @@ describe DefineOptionTestResource do
|
|
40
40
|
context "with correct type value" do
|
41
41
|
subject do
|
42
42
|
described_class.new(double(:recipe), 'resource name') do
|
43
|
-
|
44
|
-
|
43
|
+
required_attribute :required_value
|
44
|
+
typed_attribute 10
|
45
45
|
end
|
46
46
|
end
|
47
47
|
it "returns the value" do
|
48
|
-
expect(subject.
|
48
|
+
expect(subject.attributes[:typed_attribute]).to eq(10)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
context "with incorrect type value" do
|
53
53
|
subject do
|
54
54
|
described_class.new(double(:recipe), 'resource name') do
|
55
|
-
|
56
|
-
|
55
|
+
required_attribute :required_value
|
56
|
+
typed_attribute "string"
|
57
57
|
end
|
58
58
|
end
|
59
59
|
it "raises an error" do
|
60
60
|
expect do
|
61
61
|
subject
|
62
|
-
end.to raise_error(Itamae::
|
62
|
+
end.to raise_error(Itamae::Resource::InvalidTypeError)
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
@@ -68,11 +68,11 @@ describe DefineOptionTestResource do
|
|
68
68
|
context "without setting the value" do
|
69
69
|
subject do
|
70
70
|
described_class.new(double(:recipe), 'resource name') do
|
71
|
-
|
71
|
+
required_attribute :required_value
|
72
72
|
end
|
73
73
|
end
|
74
74
|
it "returns the resource name" do
|
75
|
-
expect(subject.
|
75
|
+
expect(subject.attributes[:default_name_attribute]).
|
76
76
|
to eq("resource name")
|
77
77
|
end
|
78
78
|
end
|
@@ -80,9 +80,9 @@ describe DefineOptionTestResource do
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
class TestResource < Itamae::
|
84
|
-
|
85
|
-
|
83
|
+
class TestResource < Itamae::Resource::Base
|
84
|
+
define_attribute :action, default: :create
|
85
|
+
define_attribute :attribute_key, required: false
|
86
86
|
end
|
87
87
|
|
88
88
|
describe TestResource do
|
@@ -116,19 +116,19 @@ describe TestResource do
|
|
116
116
|
|
117
117
|
describe "#run_specinfra" do
|
118
118
|
it "runs specinfra's command by specinfra's backend" do
|
119
|
-
expect(Specinfra.command).to receive(:cmd).and_return("command")
|
119
|
+
expect(Specinfra.command).to receive(:get).with(:cmd).and_return("command")
|
120
120
|
expect(Itamae.backend).to receive(:run_command).with("command").
|
121
121
|
and_return(Specinfra::CommandResult.new(exit_status: 0))
|
122
122
|
subject.send(:run_specinfra, :cmd)
|
123
123
|
end
|
124
124
|
context "when the command execution failed" do
|
125
125
|
it "raises CommandExecutionError" do
|
126
|
-
expect(Specinfra.command).to receive(:cmd).and_return("command")
|
126
|
+
expect(Specinfra.command).to receive(:get).with(:cmd).and_return("command")
|
127
127
|
expect(Itamae.backend).to receive(:run_command).with("command").
|
128
128
|
and_return(Specinfra::CommandResult.new(exit_status: 1))
|
129
129
|
expect do
|
130
130
|
subject.send(:run_specinfra, :cmd)
|
131
|
-
end.to raise_error(Itamae::
|
131
|
+
end.to raise_error(Itamae::Resource::CommandExecutionError)
|
132
132
|
end
|
133
133
|
end
|
134
134
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Itamae
|
4
|
+
describe Resource do
|
5
|
+
describe ".get_resource_class_name" do
|
6
|
+
let(:method) { :foo_bar_baz }
|
7
|
+
it "returns camel-cased string" do
|
8
|
+
expect(described_class.get_resource_class_name(method)).
|
9
|
+
to eq("FooBarBaz")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe ".parse_description" do
|
14
|
+
context "with valid description" do
|
15
|
+
it "returns type and name" do
|
16
|
+
expect(described_class.parse_description("this-is_type[this-is_name]")).
|
17
|
+
to eq(["this-is_type", "this-is_name"])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with invalid description" do
|
22
|
+
it "raises an error" do
|
23
|
+
expect do
|
24
|
+
described_class.parse_description("[this-is_type][this-is_name]")
|
25
|
+
end.to raise_error(Itamae::Resource::ParseError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: itamae
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryota Arai
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.0.0.
|
33
|
+
version: 2.0.0.beta32
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2.0.0.
|
40
|
+
version: 2.0.0.beta32
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: hashie
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: ansi
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: net-scp
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -161,13 +175,16 @@ files:
|
|
161
175
|
- lib/itamae/logger.rb
|
162
176
|
- lib/itamae/node.rb
|
163
177
|
- lib/itamae/recipe.rb
|
164
|
-
- lib/itamae/
|
165
|
-
- lib/itamae/
|
166
|
-
- lib/itamae/
|
167
|
-
- lib/itamae/
|
168
|
-
- lib/itamae/
|
169
|
-
- lib/itamae/
|
170
|
-
- lib/itamae/
|
178
|
+
- lib/itamae/resource.rb
|
179
|
+
- lib/itamae/resource/base.rb
|
180
|
+
- lib/itamae/resource/directory.rb
|
181
|
+
- lib/itamae/resource/execute.rb
|
182
|
+
- lib/itamae/resource/file.rb
|
183
|
+
- lib/itamae/resource/mail_alias.rb
|
184
|
+
- lib/itamae/resource/package.rb
|
185
|
+
- lib/itamae/resource/remote_file.rb
|
186
|
+
- lib/itamae/resource/template.rb
|
187
|
+
- lib/itamae/resource_collection.rb
|
171
188
|
- lib/itamae/runner.rb
|
172
189
|
- lib/itamae/specinfra.rb
|
173
190
|
- lib/itamae/version.rb
|
@@ -181,10 +198,10 @@ files:
|
|
181
198
|
- spec/unit/lib/itamae/logger_spec.rb
|
182
199
|
- spec/unit/lib/itamae/node_spec.rb
|
183
200
|
- spec/unit/lib/itamae/recipe_spec.rb
|
184
|
-
- spec/unit/lib/itamae/
|
185
|
-
- spec/unit/lib/itamae/
|
186
|
-
- spec/unit/lib/itamae/
|
187
|
-
- spec/unit/lib/itamae/
|
201
|
+
- spec/unit/lib/itamae/resource/base_spec.rb
|
202
|
+
- spec/unit/lib/itamae/resource/package_spec.rb
|
203
|
+
- spec/unit/lib/itamae/resource/remote_file_spec.rb
|
204
|
+
- spec/unit/lib/itamae/resource_spec.rb
|
188
205
|
- spec/unit/lib/itamae/runner_spec.rb
|
189
206
|
- spec/unit/spec_helper.rb
|
190
207
|
homepage: https://github.com/ryotarai/itamae
|
@@ -222,9 +239,9 @@ test_files:
|
|
222
239
|
- spec/unit/lib/itamae/logger_spec.rb
|
223
240
|
- spec/unit/lib/itamae/node_spec.rb
|
224
241
|
- spec/unit/lib/itamae/recipe_spec.rb
|
225
|
-
- spec/unit/lib/itamae/
|
226
|
-
- spec/unit/lib/itamae/
|
227
|
-
- spec/unit/lib/itamae/
|
228
|
-
- spec/unit/lib/itamae/
|
242
|
+
- spec/unit/lib/itamae/resource/base_spec.rb
|
243
|
+
- spec/unit/lib/itamae/resource/package_spec.rb
|
244
|
+
- spec/unit/lib/itamae/resource/remote_file_spec.rb
|
245
|
+
- spec/unit/lib/itamae/resource_spec.rb
|
229
246
|
- spec/unit/lib/itamae/runner_spec.rb
|
230
247
|
- spec/unit/spec_helper.rb
|
data/lib/itamae/resources.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'itamae'
|
2
|
-
require 'itamae/resources/base'
|
3
|
-
require 'itamae/resources/file'
|
4
|
-
require 'itamae/resources/package'
|
5
|
-
require 'itamae/resources/remote_file'
|
6
|
-
require 'itamae/resources/directory'
|
7
|
-
require 'itamae/resources/template'
|
8
|
-
|
9
|
-
module Itamae
|
10
|
-
module Resources
|
11
|
-
Error = Class.new(StandardError)
|
12
|
-
CommandExecutionError = Class.new(StandardError)
|
13
|
-
OptionMissingError = Class.new(StandardError)
|
14
|
-
InvalidTypeError = Class.new(StandardError)
|
15
|
-
NotSupportedOsError = Class.new(StandardError)
|
16
|
-
|
17
|
-
def self.get_resource_class_name(method)
|
18
|
-
method.to_s.split('_').map {|part| part.capitalize}.join
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.get_resource_class(method)
|
22
|
-
const_get(get_resource_class_name(method))
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,151 +0,0 @@
|
|
1
|
-
require 'itamae'
|
2
|
-
require 'shellwords'
|
3
|
-
|
4
|
-
module Itamae
|
5
|
-
module Resources
|
6
|
-
class Base
|
7
|
-
@defined_options ||= {}
|
8
|
-
@supported_oses ||= []
|
9
|
-
|
10
|
-
class << self
|
11
|
-
attr_reader :defined_options
|
12
|
-
attr_reader :supported_oses
|
13
|
-
|
14
|
-
def inherited(subclass)
|
15
|
-
subclass.instance_variable_set(
|
16
|
-
:@defined_options,
|
17
|
-
self.defined_options.dup
|
18
|
-
)
|
19
|
-
end
|
20
|
-
|
21
|
-
def define_option(name, options)
|
22
|
-
current = @defined_options[name.to_sym] || {}
|
23
|
-
@defined_options[name.to_sym] = current.merge(options)
|
24
|
-
end
|
25
|
-
|
26
|
-
def support_os(hash)
|
27
|
-
@supported_oses << hash
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
define_option :action, type: Symbol, required: true
|
32
|
-
|
33
|
-
attr_reader :resource_name
|
34
|
-
attr_reader :options
|
35
|
-
|
36
|
-
def initialize(recipe, resource_name, &block)
|
37
|
-
@options = {}
|
38
|
-
@recipe = recipe
|
39
|
-
@resource_name = resource_name
|
40
|
-
|
41
|
-
instance_eval(&block) if block_given?
|
42
|
-
|
43
|
-
process_options
|
44
|
-
ensure_os
|
45
|
-
end
|
46
|
-
|
47
|
-
def run
|
48
|
-
public_send("#{action}_action".to_sym)
|
49
|
-
end
|
50
|
-
|
51
|
-
def nothing_action
|
52
|
-
# do nothing
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def method_missing(method, *args)
|
58
|
-
if args.size == 1 && self.class.defined_options[method]
|
59
|
-
return @options[method] = args.first
|
60
|
-
elsif args.size == 0 && @options.has_key?(method)
|
61
|
-
return @options[method]
|
62
|
-
end
|
63
|
-
super
|
64
|
-
end
|
65
|
-
|
66
|
-
def process_options
|
67
|
-
self.class.defined_options.each_pair do |key, details|
|
68
|
-
@options[key] ||= @resource_name if details[:default_name]
|
69
|
-
@options[key] ||= details[:default]
|
70
|
-
|
71
|
-
if details[:required] && !@options[key]
|
72
|
-
raise Resources::OptionMissingError, "'#{key}' option is required but it is not set."
|
73
|
-
end
|
74
|
-
|
75
|
-
if @options[key] && details[:type] && !@options[key].is_a?(details[:type])
|
76
|
-
raise Resources::InvalidTypeError, "#{key} option should be #{details[:type]}."
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def run_specinfra(type, *args)
|
82
|
-
command = Specinfra.command.public_send(type, *args)
|
83
|
-
run_command(command)
|
84
|
-
end
|
85
|
-
|
86
|
-
def run_command(command)
|
87
|
-
result = backend.run_command(command)
|
88
|
-
exit_status = result.exit_status
|
89
|
-
|
90
|
-
if exit_status == 0
|
91
|
-
method = :debug
|
92
|
-
Logger.public_send(method, "Command `#{command}` succeeded")
|
93
|
-
else
|
94
|
-
method = :error
|
95
|
-
Logger.public_send(method, "Command `#{command}` failed. (exit status: #{exit_status})")
|
96
|
-
end
|
97
|
-
|
98
|
-
if result.stdout && result.stdout != ''
|
99
|
-
Logger.public_send(method, "STDOUT> #{result.stdout.chomp}")
|
100
|
-
end
|
101
|
-
if result.stderr && result.stderr != ''
|
102
|
-
Logger.public_send(method, "STDERR> #{result.stderr.chomp}")
|
103
|
-
end
|
104
|
-
|
105
|
-
unless exit_status == 0
|
106
|
-
raise CommandExecutionError
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def copy_file(src, dst)
|
111
|
-
Logger.debug "Copying a file from '#{src}' to '#{dst}'..."
|
112
|
-
unless ::File.exist?(src)
|
113
|
-
raise Error, "The file '#{src}' doesn't exist."
|
114
|
-
end
|
115
|
-
unless backend.copy_file(src, dst)
|
116
|
-
raise Error, "Copying a file failed."
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def node
|
121
|
-
runner.node
|
122
|
-
end
|
123
|
-
|
124
|
-
def backend
|
125
|
-
Itamae.backend
|
126
|
-
end
|
127
|
-
|
128
|
-
def runner
|
129
|
-
@recipe.runner
|
130
|
-
end
|
131
|
-
|
132
|
-
def shell_escape(str)
|
133
|
-
Shellwords.escape(str)
|
134
|
-
end
|
135
|
-
|
136
|
-
def ensure_os
|
137
|
-
return unless self.class.supported_oses
|
138
|
-
ok = self.class.supported_oses.any? do |supported|
|
139
|
-
supported.each_pair.all? do |k, v|
|
140
|
-
backend.os[k] == v
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
unless ok
|
145
|
-
raise NotSupportedOsError, "#{self.class.name} resource doesn't support this OS now."
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'itamae'
|
2
|
-
|
3
|
-
module Itamae
|
4
|
-
module Resources
|
5
|
-
class Directory < Base
|
6
|
-
define_option :action, default: :create
|
7
|
-
define_option :path, type: String, default_name: true
|
8
|
-
define_option :mode, type: String
|
9
|
-
define_option :owner, type: String
|
10
|
-
define_option :group, type: String
|
11
|
-
|
12
|
-
def create_action
|
13
|
-
if ! backend.check_file_is_directory(path)
|
14
|
-
backend.create_file_as_directory(path)
|
15
|
-
end
|
16
|
-
if options[:mode]
|
17
|
-
backend.change_file_mode(path, options[:mode])
|
18
|
-
end
|
19
|
-
if options[:owner] || options[:group]
|
20
|
-
backend.change_file_owner(path, options[:owner], options[:group])
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Itamae
|
4
|
-
describe Resources do
|
5
|
-
describe "#get_resource_class_name" do
|
6
|
-
let(:method) { :foo_bar_baz }
|
7
|
-
it "returns camel-cased string" do
|
8
|
-
expect(described_class.get_resource_class_name(method)).
|
9
|
-
to eq("FooBarBaz")
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|