itamae-mitsurin 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +26 -0
- data/README.md +44 -0
- data/Rakefile +55 -0
- data/bin/itamae +5 -0
- data/bin/itamae-mitsurin +5 -0
- data/itamae-mitsurin.gemspec +35 -0
- data/lib/itamae.rb +18 -0
- data/lib/itamae/backend.rb +293 -0
- data/lib/itamae/cli.rb +86 -0
- data/lib/itamae/definition.rb +40 -0
- data/lib/itamae/ext.rb +1 -0
- data/lib/itamae/ext/specinfra.rb +39 -0
- data/lib/itamae/generators.rb +20 -0
- data/lib/itamae/generators/cookbook.rb +22 -0
- data/lib/itamae/generators/project.rb +22 -0
- data/lib/itamae/generators/role.rb +22 -0
- data/lib/itamae/generators/templates/cookbook/default.rb +0 -0
- data/lib/itamae/generators/templates/cookbook/files/.keep +0 -0
- data/lib/itamae/generators/templates/cookbook/templates/.keep +0 -0
- data/lib/itamae/generators/templates/project/Gemfile +4 -0
- data/lib/itamae/generators/templates/project/cookbooks/.keep +0 -0
- data/lib/itamae/generators/templates/project/roles/.keep +0 -0
- data/lib/itamae/generators/templates/role/default.rb +0 -0
- data/lib/itamae/generators/templates/role/files/.keep +0 -0
- data/lib/itamae/generators/templates/role/templates/.keep +0 -0
- data/lib/itamae/handler.rb +21 -0
- data/lib/itamae/handler/base.rb +40 -0
- data/lib/itamae/handler/debug.rb +10 -0
- data/lib/itamae/handler/fluentd.rb +44 -0
- data/lib/itamae/handler/json.rb +22 -0
- data/lib/itamae/handler_proxy.rb +38 -0
- data/lib/itamae/logger.rb +124 -0
- data/lib/itamae/mitsurin.rb +13 -0
- data/lib/itamae/mitsurin/cli.rb +56 -0
- data/lib/itamae/mitsurin/creators.rb +19 -0
- data/lib/itamae/mitsurin/creators/cookbook.rb +24 -0
- data/lib/itamae/mitsurin/creators/project.rb +24 -0
- data/lib/itamae/mitsurin/creators/templates/project/.rspec +2 -0
- data/lib/itamae/mitsurin/creators/templates/project/Gemfile +3 -0
- data/lib/itamae/mitsurin/creators/templates/project/Rakefile +2 -0
- data/lib/itamae/mitsurin/creators/templates/project/environments/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/environments/sample.json +7 -0
- data/lib/itamae/mitsurin/creators/templates/project/nodes/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/nodes/sample01.json +8 -0
- data/lib/itamae/mitsurin/creators/templates/project/roles/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/attributes/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/files/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/recipes/default.rb +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/spec/default_spec.rb +2 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/templates/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/spec/spec_helper.rb +32 -0
- data/lib/itamae/mitsurin/creators/templates/project/tmp-nodes/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/attributes/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/files/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/recipes/default.rb +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/spec/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/templates/.keep +0 -0
- data/lib/itamae/mitsurin/itamae_task.rb +199 -0
- data/lib/itamae/mitsurin/serverspec_task.rb +110 -0
- data/lib/itamae/mitsurin/version.rb +5 -0
- data/lib/itamae/mitsurin/version.txt +1 -0
- data/lib/itamae/node.rb +74 -0
- data/lib/itamae/notification.rb +46 -0
- data/lib/itamae/recipe.rb +171 -0
- data/lib/itamae/recipe_children.rb +86 -0
- data/lib/itamae/resource.rb +73 -0
- data/lib/itamae/resource/aws_ebs_volume.rb +84 -0
- data/lib/itamae/resource/base.rb +374 -0
- data/lib/itamae/resource/directory.rb +63 -0
- data/lib/itamae/resource/execute.rb +26 -0
- data/lib/itamae/resource/file.rb +176 -0
- data/lib/itamae/resource/gem_package.rb +81 -0
- data/lib/itamae/resource/git.rb +94 -0
- data/lib/itamae/resource/group.rb +42 -0
- data/lib/itamae/resource/http_request.rb +71 -0
- data/lib/itamae/resource/link.rb +33 -0
- data/lib/itamae/resource/local_ruby_block.rb +15 -0
- data/lib/itamae/resource/package.rb +44 -0
- data/lib/itamae/resource/remote_directory.rb +84 -0
- data/lib/itamae/resource/remote_file.rb +54 -0
- data/lib/itamae/resource/service.rb +69 -0
- data/lib/itamae/resource/template.rb +53 -0
- data/lib/itamae/resource/user.rb +93 -0
- data/lib/itamae/runner.rb +124 -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 +393 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
require 'itamae/mitsurin'
|
3
|
+
require 'itamae/resource/base'
|
4
|
+
require 'itamae/resource/file'
|
5
|
+
require 'itamae/resource/package'
|
6
|
+
require 'itamae/resource/remote_directory'
|
7
|
+
require 'itamae/resource/remote_file'
|
8
|
+
require 'itamae/resource/directory'
|
9
|
+
require 'itamae/resource/template'
|
10
|
+
require 'itamae/resource/http_request'
|
11
|
+
require 'itamae/resource/execute'
|
12
|
+
require 'itamae/resource/service'
|
13
|
+
require 'itamae/resource/link'
|
14
|
+
require 'itamae/resource/local_ruby_block'
|
15
|
+
require 'itamae/resource/git'
|
16
|
+
require 'itamae/resource/user'
|
17
|
+
require 'itamae/resource/group'
|
18
|
+
require 'itamae/resource/gem_package'
|
19
|
+
require 'itamae/resource/aws_ebs_volume'
|
20
|
+
|
21
|
+
module Itamae
|
22
|
+
module Resource
|
23
|
+
Error = Class.new(StandardError)
|
24
|
+
AttributeMissingError = Class.new(StandardError)
|
25
|
+
InvalidTypeError = Class.new(StandardError)
|
26
|
+
ParseError = Class.new(StandardError)
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def to_camel_case(str)
|
30
|
+
str.split('_').map {|part| part.capitalize}.join
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_resource_class(method)
|
34
|
+
begin
|
35
|
+
self.const_get(to_camel_case(method.to_s))
|
36
|
+
rescue NameError
|
37
|
+
begin
|
38
|
+
::Itamae::Plugin::Resource.const_get(to_camel_case(method.to_s))
|
39
|
+
rescue NameError
|
40
|
+
autoload_plugin_resource(method)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def autoload_plugin_resource(method)
|
46
|
+
begin
|
47
|
+
require "itamae/plugin/resource/#{method}"
|
48
|
+
::Itamae::Plugin::Resource.const_get(to_camel_case(method.to_s))
|
49
|
+
rescue LoadError, NameError
|
50
|
+
raise Error, "#{method} resource is missing."
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def define_resource(name, klass)
|
55
|
+
class_name = to_camel_case(name.to_s)
|
56
|
+
if Resource.const_defined?(class_name)
|
57
|
+
Itamae.logger.warn "Redefine class. (#{class_name})"
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
Resource.const_set(class_name, klass)
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_description(desc)
|
65
|
+
if /\A([^\[]+)\[([^\]]+)\]\z/ =~ desc
|
66
|
+
[$1, $2]
|
67
|
+
else
|
68
|
+
raise ParseError, "'#{desc}' doesn't represent a resource."
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
require 'itamae/mitsurin'
|
3
|
+
|
4
|
+
module Itamae
|
5
|
+
module Resource
|
6
|
+
class AwsEbsVolume < Base
|
7
|
+
define_attribute :action, default: :create
|
8
|
+
define_attribute :name, type: String, default_name: true
|
9
|
+
define_attribute :availability_zone, type: String
|
10
|
+
define_attribute :device, type: String
|
11
|
+
define_attribute :instance_id, type: String
|
12
|
+
define_attribute :volume_type, type: String
|
13
|
+
define_attribute :size, type: Integer
|
14
|
+
|
15
|
+
def action_create(options)
|
16
|
+
ec2 = ::Aws::EC2::Client.new
|
17
|
+
volumes = ec2.describe_volumes(
|
18
|
+
{
|
19
|
+
filters: [
|
20
|
+
{
|
21
|
+
name: 'tag:Name',
|
22
|
+
values: [ attributes.name ],
|
23
|
+
},
|
24
|
+
],
|
25
|
+
}
|
26
|
+
).volumes
|
27
|
+
|
28
|
+
if volumes.empty?
|
29
|
+
@volume = ec2.create_volume(
|
30
|
+
size: attributes[:size], # attributes.size returns the size of attributes hash
|
31
|
+
availability_zone: attributes.availability_zone,
|
32
|
+
volume_type: attributes.volume_type,
|
33
|
+
)
|
34
|
+
|
35
|
+
ec2.create_tags(
|
36
|
+
{
|
37
|
+
resources: [ @volume.volume_id ],
|
38
|
+
tags: [
|
39
|
+
{
|
40
|
+
key: 'Name',
|
41
|
+
value: attributes.name,
|
42
|
+
},
|
43
|
+
],
|
44
|
+
}
|
45
|
+
)
|
46
|
+
|
47
|
+
updated!
|
48
|
+
sleep(3)
|
49
|
+
else
|
50
|
+
@volume = volumes[0]
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def action_attach(options)
|
56
|
+
ec2 = ::Aws::EC2::Client.new
|
57
|
+
volumes = ec2.describe_volumes(
|
58
|
+
{
|
59
|
+
filters: [
|
60
|
+
{
|
61
|
+
name: 'tag:Name',
|
62
|
+
values: [ attributes.name ],
|
63
|
+
},
|
64
|
+
],
|
65
|
+
}
|
66
|
+
).volumes
|
67
|
+
|
68
|
+
unless volumes.empty?
|
69
|
+
@volume = ec2.attach_volume({
|
70
|
+
volume_id: @volume.volume_id,
|
71
|
+
instance_id: attributes.instance_id,
|
72
|
+
device: attributes.device
|
73
|
+
})
|
74
|
+
|
75
|
+
updated!
|
76
|
+
else
|
77
|
+
@volume = volumes[0]
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,374 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
require 'shellwords'
|
3
|
+
require 'hashie'
|
4
|
+
|
5
|
+
module Itamae
|
6
|
+
module Resource
|
7
|
+
class Base
|
8
|
+
class EvalContext
|
9
|
+
attr_reader :attributes
|
10
|
+
attr_reader :notifications
|
11
|
+
attr_reader :subscriptions
|
12
|
+
attr_reader :verify_commands
|
13
|
+
attr_reader :only_if_command
|
14
|
+
attr_reader :not_if_command
|
15
|
+
|
16
|
+
def initialize(resource)
|
17
|
+
@resource = resource
|
18
|
+
|
19
|
+
@attributes = Hashie::Mash.new
|
20
|
+
@notifications = []
|
21
|
+
@subscriptions = []
|
22
|
+
@verify_commands = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond_to_missing?(method, include_private = false)
|
26
|
+
@resource.class.defined_attributes.has_key?(method) || super
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(method, *args, &block)
|
30
|
+
if @resource.class.defined_attributes[method]
|
31
|
+
if args.size == 1
|
32
|
+
return @attributes[method] = args.first
|
33
|
+
elsif args.size == 0 && block_given?
|
34
|
+
return @attributes[method] = block
|
35
|
+
elsif args.size == 0
|
36
|
+
return @attributes[method]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def notifies(action, resource_desc, timing = :delay)
|
44
|
+
@notifications << Notification.create(@resource, action, resource_desc, timing)
|
45
|
+
end
|
46
|
+
|
47
|
+
def subscribes(action, resource_desc, timing = :delay)
|
48
|
+
@subscriptions << Subscription.create(@resource, action, resource_desc, timing)
|
49
|
+
end
|
50
|
+
|
51
|
+
def only_if(command)
|
52
|
+
@only_if_command = command
|
53
|
+
end
|
54
|
+
|
55
|
+
def not_if(command)
|
56
|
+
@not_if_command = command
|
57
|
+
end
|
58
|
+
|
59
|
+
def node
|
60
|
+
@resource.recipe.runner.node
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_command(*args)
|
64
|
+
@resource.recipe.runner.backend.run_command(*args)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Experimental
|
68
|
+
def verify(command)
|
69
|
+
@verify_commands << command
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
@defined_attributes ||= {}
|
74
|
+
|
75
|
+
class << self
|
76
|
+
attr_reader :defined_attributes
|
77
|
+
attr_reader :supported_oses
|
78
|
+
|
79
|
+
def inherited(subclass)
|
80
|
+
subclass.instance_variable_set(
|
81
|
+
:@defined_attributes,
|
82
|
+
self.defined_attributes.dup
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
def define_attribute(name, options)
|
87
|
+
current = @defined_attributes[name.to_sym] || {}
|
88
|
+
@defined_attributes[name.to_sym] = current.merge(options)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
define_attribute :action, type: [Symbol, Array], required: true
|
93
|
+
define_attribute :user, type: String
|
94
|
+
define_attribute :cwd, type: String
|
95
|
+
|
96
|
+
attr_reader :recipe
|
97
|
+
attr_reader :resource_name
|
98
|
+
attr_reader :attributes
|
99
|
+
attr_reader :current_attributes
|
100
|
+
attr_reader :subscriptions
|
101
|
+
attr_reader :notifications
|
102
|
+
attr_reader :updated
|
103
|
+
|
104
|
+
def initialize(recipe, resource_name, &block)
|
105
|
+
clear_current_attributes
|
106
|
+
@recipe = recipe
|
107
|
+
@resource_name = resource_name
|
108
|
+
@updated = false
|
109
|
+
|
110
|
+
EvalContext.new(self).tap do |context|
|
111
|
+
context.instance_eval(&block) if block
|
112
|
+
@attributes = context.attributes
|
113
|
+
@notifications = context.notifications
|
114
|
+
@subscriptions = context.subscriptions
|
115
|
+
@only_if_command = context.only_if_command
|
116
|
+
@not_if_command = context.not_if_command
|
117
|
+
@verify_commands = context.verify_commands
|
118
|
+
end
|
119
|
+
|
120
|
+
process_attributes
|
121
|
+
end
|
122
|
+
|
123
|
+
def run(specific_action = nil)
|
124
|
+
runner.handler.event(:resource, resource_type: resource_type, resource_name: resource_name) do
|
125
|
+
Itamae.logger.debug "#{resource_type}[#{resource_name}]"
|
126
|
+
|
127
|
+
Itamae.logger.with_indent_if(Itamae.logger.debug?) do
|
128
|
+
if do_not_run_because_of_only_if?
|
129
|
+
Itamae.logger.debug "#{resource_type}[#{resource_name}] Execution skipped because of only_if attribute"
|
130
|
+
return
|
131
|
+
elsif do_not_run_because_of_not_if?
|
132
|
+
Itamae.logger.debug "#{resource_type}[#{resource_name}] Execution skipped because of not_if attribute"
|
133
|
+
return
|
134
|
+
end
|
135
|
+
|
136
|
+
[specific_action || attributes.action].flatten.each do |action|
|
137
|
+
run_action(action)
|
138
|
+
end
|
139
|
+
|
140
|
+
verify unless runner.dry_run?
|
141
|
+
if updated?
|
142
|
+
notify
|
143
|
+
runner.handler.event(:resource_updated)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
@updated = false
|
148
|
+
end
|
149
|
+
rescue Backend::CommandExecutionError
|
150
|
+
Itamae.logger.error "#{resource_type}[#{resource_name}] Failed."
|
151
|
+
exit 2
|
152
|
+
end
|
153
|
+
|
154
|
+
def action_nothing
|
155
|
+
# do nothing
|
156
|
+
end
|
157
|
+
|
158
|
+
def resource_type
|
159
|
+
self.class.name.split("::").last.scan(/[A-Z][^A-Z]+/).map(&:downcase).join('_')
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
alias_method :current, :current_attributes
|
165
|
+
|
166
|
+
def run_action(action)
|
167
|
+
runner.handler.event(:action, action: action) do
|
168
|
+
original_attributes = @attributes # preserve and restore later
|
169
|
+
@current_action = action
|
170
|
+
|
171
|
+
clear_current_attributes
|
172
|
+
|
173
|
+
Itamae.logger.debug "#{resource_type}[#{resource_name}] action: #{action}"
|
174
|
+
|
175
|
+
return if action == :nothing
|
176
|
+
|
177
|
+
Itamae.logger.with_indent_if(Itamae.logger.debug?) do
|
178
|
+
Itamae.logger.debug "(in pre_action)"
|
179
|
+
pre_action
|
180
|
+
|
181
|
+
Itamae.logger.debug "(in set_current_attributes)"
|
182
|
+
set_current_attributes
|
183
|
+
|
184
|
+
Itamae.logger.debug "(in show_differences)"
|
185
|
+
show_differences
|
186
|
+
|
187
|
+
method_name = "action_#{action}"
|
188
|
+
if runner.dry_run?
|
189
|
+
unless respond_to?(method_name)
|
190
|
+
Itamae.logger.error "action #{action.inspect} is unavailable"
|
191
|
+
end
|
192
|
+
else
|
193
|
+
args = [method_name]
|
194
|
+
if method(method_name).arity == 1
|
195
|
+
# for plugin compatibility
|
196
|
+
args << runner.options
|
197
|
+
end
|
198
|
+
|
199
|
+
public_send(*args)
|
200
|
+
end
|
201
|
+
|
202
|
+
if different?
|
203
|
+
updated!
|
204
|
+
runner.handler.event(:attribute_changed, from: @current_attributes, to: @attributes)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
@current_action = nil
|
209
|
+
@attributes = original_attributes
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def clear_current_attributes
|
214
|
+
@current_attributes = Hashie::Mash.new
|
215
|
+
end
|
216
|
+
|
217
|
+
def pre_action
|
218
|
+
# do nothing
|
219
|
+
end
|
220
|
+
|
221
|
+
def set_current_attributes
|
222
|
+
# do nothing
|
223
|
+
end
|
224
|
+
|
225
|
+
def different?
|
226
|
+
@current_attributes.each_pair.any? do |key, current_value|
|
227
|
+
!current_value.nil? &&
|
228
|
+
!@attributes[key].nil? &&
|
229
|
+
current_value != @attributes[key]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def show_differences
|
234
|
+
@current_attributes.each_pair do |key, current_value|
|
235
|
+
value = @attributes[key]
|
236
|
+
if current_value.nil? && value.nil?
|
237
|
+
# ignore
|
238
|
+
elsif current_value.nil? && !value.nil?
|
239
|
+
Itamae.logger.color :green do
|
240
|
+
Itamae.logger.info "#{resource_type}[#{resource_name}] #{key} will be '#{value}'"
|
241
|
+
end
|
242
|
+
elsif current_value == value || value.nil?
|
243
|
+
Itamae.logger.debug "#{resource_type}[#{resource_name}] #{key} will not change (current value is '#{current_value}')"
|
244
|
+
else
|
245
|
+
Itamae.logger.color :green do
|
246
|
+
Itamae.logger.info "#{resource_type}[#{resource_name}] #{key} will change from '#{current_value}' to '#{value}'"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def process_attributes
|
253
|
+
self.class.defined_attributes.each_pair do |key, details|
|
254
|
+
@attributes[key] ||= @resource_name if details[:default_name]
|
255
|
+
@attributes[key] = details[:default] if details.has_key?(:default) && !@attributes.has_key?(key)
|
256
|
+
|
257
|
+
if details[:required] && !@attributes[key]
|
258
|
+
raise Resource::AttributeMissingError, "'#{key}' attribute is required but it is not set."
|
259
|
+
end
|
260
|
+
|
261
|
+
if @attributes[key] && details[:type]
|
262
|
+
valid_type = [details[:type]].flatten.any? do |type|
|
263
|
+
@attributes[key].is_a?(type)
|
264
|
+
end
|
265
|
+
unless valid_type
|
266
|
+
raise Resource::InvalidTypeError, "#{key} attribute should be #{details[:type]}."
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def do_not_run_because_of_only_if?
|
273
|
+
@only_if_command &&
|
274
|
+
run_command(@only_if_command, error: false).exit_status != 0
|
275
|
+
end
|
276
|
+
|
277
|
+
def do_not_run_because_of_not_if?
|
278
|
+
@not_if_command &&
|
279
|
+
run_command(@not_if_command, error: false).exit_status == 0
|
280
|
+
end
|
281
|
+
|
282
|
+
def backend
|
283
|
+
runner.backend
|
284
|
+
end
|
285
|
+
|
286
|
+
def runner
|
287
|
+
recipe.runner
|
288
|
+
end
|
289
|
+
|
290
|
+
def node
|
291
|
+
runner.node
|
292
|
+
end
|
293
|
+
|
294
|
+
def run_command(*args)
|
295
|
+
unless args.last.is_a?(Hash)
|
296
|
+
args << {}
|
297
|
+
end
|
298
|
+
|
299
|
+
args.last[:user] ||= attributes.user
|
300
|
+
args.last[:cwd] ||= attributes.cwd
|
301
|
+
|
302
|
+
backend.run_command(*args)
|
303
|
+
end
|
304
|
+
|
305
|
+
def check_command(*args)
|
306
|
+
unless args.last.is_a?(Hash)
|
307
|
+
args << {}
|
308
|
+
end
|
309
|
+
|
310
|
+
args.last[:error] = false
|
311
|
+
|
312
|
+
run_command(*args).exit_status == 0
|
313
|
+
end
|
314
|
+
|
315
|
+
def run_specinfra(type, *args)
|
316
|
+
command = backend.get_command(type, *args)
|
317
|
+
|
318
|
+
if type.to_s.start_with?("check_")
|
319
|
+
check_command(command)
|
320
|
+
else
|
321
|
+
run_command(command)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def shell_escape(str)
|
326
|
+
str.shellescape
|
327
|
+
end
|
328
|
+
|
329
|
+
def updated!
|
330
|
+
Itamae.logger.debug "This resource is updated."
|
331
|
+
@updated = true
|
332
|
+
end
|
333
|
+
|
334
|
+
def updated?
|
335
|
+
@updated
|
336
|
+
end
|
337
|
+
|
338
|
+
def notify
|
339
|
+
(notifications + recipe.children.subscribing(self)).each do |notification|
|
340
|
+
message = "Notifying #{notification.action} to #{notification.action_resource.resource_type} resource '#{notification.action_resource.resource_name}'"
|
341
|
+
|
342
|
+
if notification.delayed?
|
343
|
+
message << " (delayed)"
|
344
|
+
elsif notification.immediately?
|
345
|
+
message << " (immediately)"
|
346
|
+
end
|
347
|
+
|
348
|
+
Itamae.logger.info message
|
349
|
+
|
350
|
+
if notification.instance_of?(Subscription)
|
351
|
+
Itamae.logger.info "(because it subscribes this resource)"
|
352
|
+
end
|
353
|
+
|
354
|
+
if notification.delayed?
|
355
|
+
@recipe.delayed_notifications << notification
|
356
|
+
elsif notification.immediately?
|
357
|
+
notification.run
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def verify
|
363
|
+
return if @verify_commands.empty?
|
364
|
+
|
365
|
+
Itamae.logger.info "Verifying..."
|
366
|
+
Itamae.logger.with_indent do
|
367
|
+
@verify_commands.each do |command|
|
368
|
+
run_command(command)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|