itamae 1.0.0.beta1 → 1.0.0.beta2
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 +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 [](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
|
-
|