right_link 5.9.0

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.
Files changed (199) hide show
  1. data/actors/agent_manager.rb +88 -0
  2. data/actors/instance_scheduler.rb +321 -0
  3. data/actors/instance_services.rb +64 -0
  4. data/actors/instance_setup.rb +567 -0
  5. data/bin/cloud +25 -0
  6. data/bin/cook_runner +44 -0
  7. data/bin/deploy +120 -0
  8. data/bin/enroll +385 -0
  9. data/bin/rad +32 -0
  10. data/bin/rchk +29 -0
  11. data/bin/rnac +39 -0
  12. data/bin/rs_connect +33 -0
  13. data/bin/rs_log_level +31 -0
  14. data/bin/rs_ohai +28 -0
  15. data/bin/rs_reenroll +31 -0
  16. data/bin/rs_run_recipe +34 -0
  17. data/bin/rs_run_right_script +34 -0
  18. data/bin/rs_shutdown +33 -0
  19. data/bin/rs_tag +33 -0
  20. data/bin/rs_thunk +33 -0
  21. data/bin/rstat +31 -0
  22. data/bin/system +16 -0
  23. data/ext/Rakefile +18 -0
  24. data/init/config.yml +5 -0
  25. data/init/init.rb +79 -0
  26. data/lib/chef/ohai_setup.rb +51 -0
  27. data/lib/chef/plugins/cloud.rb +91 -0
  28. data/lib/chef/plugins/cloudstack.rb +23 -0
  29. data/lib/chef/plugins/ec2.rb +23 -0
  30. data/lib/chef/plugins/linux/block_device2.rb +24 -0
  31. data/lib/chef/plugins/rackspace.rb +23 -0
  32. data/lib/chef/plugins/rightscale.rb +125 -0
  33. data/lib/chef/plugins/windows/network.rb +114 -0
  34. data/lib/chef/plugins.rb +74 -0
  35. data/lib/chef/providers/dns_dnsmadeeasy_provider.rb +81 -0
  36. data/lib/chef/providers/dns_resource.rb +100 -0
  37. data/lib/chef/providers/executable_schedule_provider.rb +70 -0
  38. data/lib/chef/providers/executable_schedule_resource.rb +144 -0
  39. data/lib/chef/providers/remote_recipe_provider.rb +86 -0
  40. data/lib/chef/providers/remote_recipe_resource.rb +101 -0
  41. data/lib/chef/providers/right_link_tag_provider.rb +73 -0
  42. data/lib/chef/providers/right_link_tag_resource.rb +59 -0
  43. data/lib/chef/providers/right_script_provider.rb +190 -0
  44. data/lib/chef/providers/right_script_resource.rb +113 -0
  45. data/lib/chef/providers/rs_shutdown_provider.rb +75 -0
  46. data/lib/chef/providers/rs_shutdown_resource.rb +55 -0
  47. data/lib/chef/providers/server_collection_provider.rb +66 -0
  48. data/lib/chef/providers/server_collection_resource.rb +93 -0
  49. data/lib/chef/providers/windows/powershell_provider.rb +151 -0
  50. data/lib/chef/providers/windows/powershell_resource.rb +111 -0
  51. data/lib/chef/providers/windows/unsupported_provider.rb +51 -0
  52. data/lib/chef/right_providers.rb +55 -0
  53. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.csproj +104 -0
  54. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.dll-Help.xml +141 -0
  55. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Exceptions.cs +182 -0
  56. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeCommand.cs +58 -0
  57. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeRequest.cs +46 -0
  58. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeResponse.cs +45 -0
  59. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceCommand.cs +58 -0
  60. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceRequest.cs +46 -0
  61. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceResponse.cs +45 -0
  62. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceCommand.cs +58 -0
  63. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceRequest.cs +46 -0
  64. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceResponse.cs +45 -0
  65. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionCommand.cs +178 -0
  66. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionRequest.cs +67 -0
  67. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionResponse.cs +58 -0
  68. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueCommandBase.cs +142 -0
  69. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueRequestBase.cs +64 -0
  70. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueResponseBase.cs +69 -0
  71. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/JsonTransport.cs +110 -0
  72. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeClient.cs +158 -0
  73. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeServer.cs +142 -0
  74. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
  75. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolConstants.cs +55 -0
  76. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolUtilities.cs +77 -0
  77. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ReadMe.txt +53 -0
  78. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeCommand.cs +59 -0
  79. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeRequest.cs +46 -0
  80. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeResponse.cs +58 -0
  81. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceCommand.cs +59 -0
  82. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceRequest.cs +46 -0
  83. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceResponse.cs +40 -0
  84. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceCommand.cs +59 -0
  85. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceRequest.cs +46 -0
  86. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceResponse.cs +40 -0
  87. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueCommandBase.cs +293 -0
  88. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueRequestBase.cs +75 -0
  89. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueResponseBase.cs +45 -0
  90. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Transport.cs +91 -0
  91. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet.sln +35 -0
  92. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Program.cs +374 -0
  93. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
  94. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/TestChefNodeCmdlet.csproj +65 -0
  95. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Program.cs +136 -0
  96. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Properties/AssemblyInfo.cs +36 -0
  97. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/ReadMe.txt +46 -0
  98. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/TestNextActionCmdlet.csproj +68 -0
  99. data/lib/chef/windows/bin/Newtonsoft.Json.dll +0 -0
  100. data/lib/chef/windows/chef_node_server.rb +463 -0
  101. data/lib/chef/windows/dynamic_powershell_provider.rb +296 -0
  102. data/lib/chef/windows/pipe_server.rb +283 -0
  103. data/lib/chef/windows/powershell_host.rb +285 -0
  104. data/lib/chef/windows/powershell_pipe_server.rb +136 -0
  105. data/lib/chef/windows/powershell_provider_base.rb +92 -0
  106. data/lib/chef/windows/scripts/run_loop.ps1 +105 -0
  107. data/lib/clouds/cloud.rb +557 -0
  108. data/lib/clouds/cloud_factory.rb +250 -0
  109. data/lib/clouds/cloud_utilities.rb +244 -0
  110. data/lib/clouds/clouds/azure.rb +106 -0
  111. data/lib/clouds/clouds/cloudstack.rb +114 -0
  112. data/lib/clouds/clouds/ec2.rb +113 -0
  113. data/lib/clouds/clouds/eucalyptus.rb +46 -0
  114. data/lib/clouds/clouds/google.rb +102 -0
  115. data/lib/clouds/clouds/none.rb +76 -0
  116. data/lib/clouds/clouds/openstack.rb +30 -0
  117. data/lib/clouds/clouds/rackspace-ng.rb +54 -0
  118. data/lib/clouds/clouds/rackspace.rb +78 -0
  119. data/lib/clouds/clouds/softlayer.rb +91 -0
  120. data/lib/clouds/metadata_formatter.rb +108 -0
  121. data/lib/clouds/metadata_provider.rb +128 -0
  122. data/lib/clouds/metadata_source.rb +87 -0
  123. data/lib/clouds/metadata_sources/certificate_metadata_source.rb +207 -0
  124. data/lib/clouds/metadata_sources/config_drive_metadata_source.rb +129 -0
  125. data/lib/clouds/metadata_sources/file_metadata_source.rb +74 -0
  126. data/lib/clouds/metadata_sources/http_metadata_source.rb +277 -0
  127. data/lib/clouds/metadata_sources/selective_metadata_source.rb +122 -0
  128. data/lib/clouds/metadata_tree_climber.rb +144 -0
  129. data/lib/clouds/metadata_writer.rb +155 -0
  130. data/lib/clouds/metadata_writers/dictionary_metadata_writer.rb +72 -0
  131. data/lib/clouds/metadata_writers/ruby_metadata_writer.rb +76 -0
  132. data/lib/clouds/metadata_writers/shell_metadata_writer.rb +121 -0
  133. data/lib/clouds/register_clouds.rb +34 -0
  134. data/lib/clouds.rb +32 -0
  135. data/lib/gem_dependencies.rb +83 -0
  136. data/lib/git_hooks/commit-msg.rb +7 -0
  137. data/lib/instance/agent_config.rb +168 -0
  138. data/lib/instance/agent_watcher.rb +233 -0
  139. data/lib/instance/audit_cook_stub.rb +104 -0
  140. data/lib/instance/audit_proxy.rb +247 -0
  141. data/lib/instance/bundle_queue.rb +104 -0
  142. data/lib/instance/cook/agent_connection.rb +109 -0
  143. data/lib/instance/cook/audit_logger.rb +165 -0
  144. data/lib/instance/cook/audit_stub.rb +142 -0
  145. data/lib/instance/cook/ca-bundle.crt +2794 -0
  146. data/lib/instance/cook/chef_state.rb +211 -0
  147. data/lib/instance/cook/cook.rb +306 -0
  148. data/lib/instance/cook/cook_state.rb +298 -0
  149. data/lib/instance/cook/cookbook_path_mapping.rb +66 -0
  150. data/lib/instance/cook/cookbook_repo_retriever.rb +190 -0
  151. data/lib/instance/cook/executable_sequence.rb +765 -0
  152. data/lib/instance/cook/external_parameter_gatherer.rb +190 -0
  153. data/lib/instance/cook/repose_downloader.rb +349 -0
  154. data/lib/instance/cook/shutdown_request_proxy.rb +121 -0
  155. data/lib/instance/cook.rb +41 -0
  156. data/lib/instance/downloader.rb +208 -0
  157. data/lib/instance/duplicable.rb +67 -0
  158. data/lib/instance/exceptions.rb +49 -0
  159. data/lib/instance/executable_sequence_proxy.rb +278 -0
  160. data/lib/instance/instance_commands.rb +577 -0
  161. data/lib/instance/instance_state.rb +633 -0
  162. data/lib/instance/json_utilities.rb +102 -0
  163. data/lib/instance/login_manager.rb +533 -0
  164. data/lib/instance/login_user_manager.rb +522 -0
  165. data/lib/instance/message_encoder.rb +118 -0
  166. data/lib/instance/multi_thread_bundle_queue.rb +232 -0
  167. data/lib/instance/operation_context.rb +60 -0
  168. data/lib/instance/options_bag.rb +65 -0
  169. data/lib/instance/payload_formatter.rb +46 -0
  170. data/lib/instance/policy.rb +53 -0
  171. data/lib/instance/policy_audit.rb +100 -0
  172. data/lib/instance/policy_manager.rb +146 -0
  173. data/lib/instance/reenroll_manager.rb +104 -0
  174. data/lib/instance/right_scripts_cookbook.rb +181 -0
  175. data/lib/instance/shutdown_request.rb +221 -0
  176. data/lib/instance/single_thread_bundle_queue.rb +189 -0
  177. data/lib/instance/volume_management.rb +450 -0
  178. data/lib/instance.rb +50 -0
  179. data/lib/repo_conf_generators/apt_conf_generators.rb +106 -0
  180. data/lib/repo_conf_generators/gem_conf_generators.rb +80 -0
  181. data/lib/repo_conf_generators/rightscale_conf_generators.rb +254 -0
  182. data/lib/repo_conf_generators/rightscale_key.pub +17 -0
  183. data/lib/repo_conf_generators/yum_conf_generators.rb +225 -0
  184. data/lib/repo_conf_generators.rb +30 -0
  185. data/lib/run_shell.rb +28 -0
  186. data/scripts/agent_checker.rb +571 -0
  187. data/scripts/agent_controller.rb +247 -0
  188. data/scripts/agent_deployer.rb +148 -0
  189. data/scripts/bundle_runner.rb +336 -0
  190. data/scripts/cloud_controller.rb +176 -0
  191. data/scripts/log_level_manager.rb +142 -0
  192. data/scripts/ohai_runner.rb +33 -0
  193. data/scripts/reenroller.rb +193 -0
  194. data/scripts/server_importer.rb +293 -0
  195. data/scripts/shutdown_client.rb +183 -0
  196. data/scripts/system_configurator.rb +367 -0
  197. data/scripts/tagger.rb +381 -0
  198. data/scripts/thunker.rb +356 -0
  199. metadata +418 -0
@@ -0,0 +1,211 @@
1
+ #
2
+ # Copyright (c) 2009-2012 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'fileutils'
24
+
25
+ module RightScale
26
+
27
+ # Manages and persists chef state (run list and attributes)
28
+ class ChefState
29
+
30
+ # Path to JSON file where current chef state is serialized
31
+ STATE_FILE = File.join(InstanceState::STATE_DIR, 'chef.js')
32
+
33
+ # Path to JSON file where past scripts are serialized
34
+ SCRIPTS_FILE = File.join(InstanceState::STATE_DIR, 'past_scripts.js')
35
+
36
+ class ChefStateNotInitialized < Exception; end
37
+
38
+ class << self
39
+ # Load chef state from file
40
+ #
41
+ # @param [String] agent_id identity
42
+ # @param [String] secret for encryption or nil
43
+ # @param [TrueClass|FalseClass] reset persisted state if true, load it otherwise
44
+ def init(agent_id, secret, reset)
45
+ return true if initialized? && !reset
46
+ @@encoder = MessageEncoder.for_agent(agent_id, secret)
47
+ @@attributes = {}
48
+ @@past_scripts = []
49
+ if reset
50
+ save_state
51
+ else
52
+ load_state
53
+ end
54
+ true
55
+ end
56
+
57
+ # Was 'init' ever called?
58
+ # Note: Accessing or setting the chef state calls 'init'
59
+ #
60
+ # === Return
61
+ # true:: If 'init' has been called
62
+ # false:: Otherwise
63
+ def initialized?
64
+ !!defined?(@@attributes)
65
+ end
66
+
67
+ # Current node attributes
68
+ #
69
+ # === Return
70
+ # attributes(Hash):: Current node attributes
71
+ def attributes
72
+ ensure_initialized
73
+ @@attributes
74
+ end
75
+
76
+ # Current list of scripts that have already executed
77
+ #
78
+ # === Return
79
+ # (Array[(String)]) Scripts that have already executed
80
+ def past_scripts
81
+ ensure_initialized
82
+ @@past_scripts
83
+ end
84
+
85
+ # Set node attributes
86
+ # Note: can't set it to nil. Setting the attributes to nil will
87
+ # cause the attributes to be initialized if they haven't yet but won't
88
+ # assign the value nil to them.
89
+ #
90
+ # === Parameters
91
+ # attributes(Hash):: Node attributes
92
+ #
93
+ # === Return
94
+ # true:: Always return true
95
+ def attributes=(val)
96
+ ensure_initialized
97
+ @@attributes = val if val
98
+ save_state
99
+ end
100
+
101
+ # Merge given attributes into node attributes
102
+ #
103
+ # === Parameters
104
+ # attribs(Hash):: Attributes to be merged
105
+ #
106
+ # === Return
107
+ # true:: Always return true
108
+ def merge_attributes(attribs)
109
+ self.attributes = ::RightSupport::Data::HashTools.deep_merge!(attributes, attribs) if attribs
110
+ true
111
+ end
112
+
113
+ # Record script execution in scripts file
114
+ #
115
+ # === Parameters
116
+ # nickname(String):: Nickname of RightScript which successfully executed
117
+ #
118
+ # === Return
119
+ # true:: If script was added to past scripts collection
120
+ # false:: If script was already in past scripts collection
121
+ def record_script_execution(nickname)
122
+ ensure_initialized
123
+ new_script = !@@past_scripts.include?(nickname)
124
+ @@past_scripts << nickname if new_script
125
+ # note that we only persist state on successful execution of bundle.
126
+ new_script
127
+ end
128
+
129
+ # Save node attributes to file
130
+ # Note: Attributes are saved only when runlist ran on default thread
131
+ #
132
+ # === Return
133
+ # true:: Always return true
134
+ def save_state
135
+ if Cook.instance.has_default_thread?
136
+ begin
137
+ FileUtils.touch(STATE_FILE)
138
+ File.chmod(0600, STATE_FILE)
139
+ write_encoded_data(STATE_FILE, { 'attributes' => @@attributes })
140
+ RightScale::JsonUtilities::write_json(SCRIPTS_FILE, @@past_scripts)
141
+ rescue Exception => e
142
+ Log.warning("Failed to save node attributes: #{e.message}")
143
+ end
144
+ end
145
+ true
146
+ end
147
+
148
+ private
149
+ def ensure_initialized
150
+ raise ChefStateNotInitialized if !initialized?
151
+ end
152
+
153
+ # Loads Chef state from file(s), if any.
154
+ #
155
+ # === Return
156
+ # always true
157
+ def load_state
158
+ # load the previously saved Chef node attributes, if any.
159
+ if File.file?(STATE_FILE)
160
+ begin
161
+ js = read_encoded_data(STATE_FILE)
162
+ @@attributes = js['attributes'] || {}
163
+ Log.debug("Successfully loaded chef state")
164
+ rescue Exception => e
165
+ Log.error("Failed to load chef state", e)
166
+ end
167
+ else
168
+ @@attributes = {}
169
+ Log.debug("No previous state to load")
170
+ end
171
+
172
+ # load the list of previously run scripts
173
+ @@past_scripts = RightScale::JsonUtilities::read_json(SCRIPTS_FILE) rescue [] if File.file?(SCRIPTS_FILE)
174
+ Log.debug("Past scripts: #{@@past_scripts.inspect}")
175
+ true
176
+ end
177
+
178
+ # Encode and save an object to a file
179
+ #
180
+ # === Parameters
181
+ # path(String):: Path to file being written
182
+ # data(Object):: Object to be saved
183
+ #
184
+ # === Return
185
+ # true:: Always return true
186
+ def write_encoded_data(path, data)
187
+ dir = File.dirname(path)
188
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
189
+ File.open(path, 'w') do |f|
190
+ f.flock(File::LOCK_EX)
191
+ f.write(@@encoder.encode(data))
192
+ end
193
+ true
194
+ end
195
+
196
+ # Read encoded content form given file and return the origin al object
197
+ #
198
+ # === Parameters
199
+ # path(String):: Path to file being written
200
+ #
201
+ # === Return
202
+ # data(Object):: Object restored from file content
203
+ def read_encoded_data(path)
204
+ File.open(path, "r") do |f|
205
+ f.flock(File::LOCK_EX)
206
+ return @@encoder.decode(f.read)
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,306 @@
1
+ #
2
+ # Copyright (c) 2010-2012 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'right_agent'
24
+ require 'right_agent/core_payload_types'
25
+
26
+
27
+ require File.normalize_path(File.join(File.dirname(__FILE__), '..', '..', 'chef', 'right_providers'))
28
+ require File.normalize_path(File.join(File.dirname(__FILE__), '..', '..', 'chef', 'plugins'))
29
+
30
+ module RightScale
31
+
32
+ class Cook
33
+
34
+ # Name of agent running the cook process
35
+ AGENT_NAME = 'instance'
36
+
37
+ # Exceptions
38
+ class TagError < Exception; end
39
+ class BlockingError < Exception; end
40
+
41
+ # Run bundle given in stdin
42
+ def run
43
+ # 1. Load configuration settings
44
+ options = OptionsBag.load
45
+ agent_id = options[:identity]
46
+ AgentConfig.root_dir = options[:root_dir]
47
+
48
+ Log.program_name = 'RightLink'
49
+ Log.facility = 'user'
50
+ Log.log_to_file_only(options[:log_to_file_only])
51
+ Log.init(agent_id, options[:log_path])
52
+ Log.level = CookState.log_level
53
+ # add an additional logger if the agent is set to log to an alternate
54
+ # location (install, operate, decommission, ...)
55
+ Log.add_logger(::Logger.new(CookState.log_file)) if CookState.log_file
56
+
57
+ Log.info("[cook] Process starting up with dev tags: [#{CookState.startup_tags.select { |tag| tag.include?(CookState::DEV_TAG_NAMESPACE)}.join(', ')}]")
58
+ fail('Missing command server listen port') unless options[:listen_port]
59
+ fail('Missing command cookie') unless options[:cookie]
60
+ @client = CommandClient.new(options[:listen_port], options[:cookie])
61
+ ShutdownRequestProxy.init(@client)
62
+
63
+ # 2. Retrieve bundle
64
+ input = gets.chomp
65
+ begin
66
+ bundle = RightScale::MessageEncoder.for_agent(agent_id).decode(input)
67
+ rescue Exception => e
68
+ fail('Invalid bundle', e.message)
69
+ end
70
+
71
+ fail('Missing bundle', 'No bundle to run') if bundle.nil?
72
+
73
+ @thread_name = bundle.runlist_policy.thread_name if bundle.respond_to?(:runlist_policy) && bundle.runlist_policy
74
+ @thread_name ||= RightScale::AgentConfig.default_thread_name
75
+ options[:thread_name] = @thread_name
76
+
77
+ # Chef state needs the server secret so it can encrypt state on disk.
78
+ # The secret is the same for all instances of the server (i.e. is still
79
+ # valid after stop and restart server).
80
+ server_secret = bundle.server_secret || AgentConfig.default_server_secret
81
+ ChefState.init(agent_id, server_secret, reset=false)
82
+
83
+ # 3. Run bundle
84
+ @@instance = self
85
+ success = nil
86
+ Log.debug("[cook] Thread name associated with bundle = #{@thread_name}")
87
+ gatherer = ExternalParameterGatherer.new(bundle, options)
88
+ sequence = ExecutableSequence.new(bundle)
89
+ EM.threadpool_size = 1
90
+ EM.error_handler do |e|
91
+ Log.error("Execution failed", e, :trace)
92
+ fail('Exception caught', "The following exception was caught during execution:\n #{e.message}")
93
+ end
94
+ EM.run do
95
+ begin
96
+ AuditStub.instance.init(options)
97
+ gatherer.callback { EM.defer { sequence.run } }
98
+ gatherer.errback { success = false; report_failure(gatherer) }
99
+ sequence.callback { success = true; send_inputs_patch(sequence) }
100
+ sequence.errback { success = false; report_failure(sequence) }
101
+
102
+ EM.defer { gatherer.run }
103
+ rescue Exception => e
104
+ fail('Execution failed', Log.format("Execution failed", e, :trace))
105
+ end
106
+ end
107
+
108
+ rescue Exception => e
109
+ fail('Execution failed', Log.format("Run failed", e, :trace))
110
+
111
+ ensure
112
+ Log.info("[cook] Process stopping")
113
+ exit(1) unless success
114
+ end
115
+
116
+ # Determines if the current cook process has the default thread for purposes
117
+ # of concurrency with non-defaulted cooks.
118
+ def has_default_thread?
119
+ ::RightScale::AgentConfig.default_thread_name == @thread_name
120
+ end
121
+
122
+ # Helper method to send a request to one or more targets with no response expected
123
+ # See InstanceCommands for details
124
+ def send_push(type, payload = nil, target = nil, opts = {})
125
+ cmd = {:name => :send_push, :type => type, :payload => payload, :target => target, :options => opts}
126
+ # Need to execute on EM main thread where command client is running
127
+ EM.next_tick { @client.send_command(cmd) }
128
+ end
129
+
130
+ # Add given tag to tags exposed by corresponding server
131
+ #
132
+ # === Parameters
133
+ # tag(String):: Tag to be added
134
+ #
135
+ # === Return
136
+ # result(Hash):: contents of response
137
+ def query_tags(tags, agent_ids=nil, timeout=120)
138
+ cmd = { :name => :query_tags, :tags => tags }
139
+ cmd[:agent_ids] = agent_ids unless agent_ids.nil? || agent_ids.empty?
140
+ response = blocking_request(cmd, timeout)
141
+ begin
142
+ result = OperationResult.from_results(load(response, "Unexpected response #{response.inspect}"))
143
+ raise TagError.new("Query tags failed: #{result.content}") unless result.success?
144
+ return result.content
145
+ rescue
146
+ raise TagError.new("Query tags failed: #{response.inspect}")
147
+ end
148
+ end
149
+
150
+ # Add given tag to tags exposed by corresponding server
151
+ #
152
+ # === Parameters
153
+ # tag(String):: Tag to be added
154
+ # timeout(Fixnum):: Number of seconds to wait for agent response
155
+ #
156
+ # === Return
157
+ # true:: Always return true
158
+ def add_tag(tag_name, timeout)
159
+ cmd = { :name => :add_tag, :tag => tag_name }
160
+ response = blocking_request(cmd, timeout)
161
+ result = OperationResult.from_results(load(response, "Unexpected response #{response.inspect}"))
162
+ if result.success?
163
+ ::Chef::Log.info("Successfully added tag #{tag_name}")
164
+ else
165
+ raise TagError.new("Add tag failed: #{result.content}")
166
+ end
167
+ true
168
+ end
169
+
170
+ # Remove given tag from tags exposed by corresponding server
171
+ #
172
+ # === Parameters
173
+ # tag(String):: Tag to be removed
174
+ # timeout(Fixnum):: Number of seconds to wait for agent response
175
+ #
176
+ # === Return
177
+ # true:: Always return true
178
+ def remove_tag(tag_name, timeout)
179
+ cmd = { :name => :remove_tag, :tag => tag_name }
180
+ response = blocking_request(cmd, timeout)
181
+ result = OperationResult.from_results(load(response, "Unexpected response #{response.inspect}"))
182
+ if result.success?
183
+ ::Chef::Log.info("Successfully removed tag #{tag_name}")
184
+ else
185
+ raise TagError.new("Remove tag failed: #{result.content}")
186
+ end
187
+ true
188
+ end
189
+
190
+ # Retrieve current instance tags
191
+ #
192
+ # === Parameters
193
+ # timeout(Fixnum):: Number of seconds to wait for agent response
194
+ def load_tags(timeout)
195
+ cmd = { :name => :get_tags }
196
+ res = blocking_request(cmd, timeout)
197
+ raise TagError.new("Retrieving current tags failed: #{res.inspect}") unless res.kind_of?(Array)
198
+
199
+ ::Chef::Log.info("Successfully loaded current tags: '#{res.join("', '")}'")
200
+ res
201
+ end
202
+
203
+ # Access cook instance from anywhere to send requests to core through
204
+ # command protocol
205
+ def self.instance
206
+ @@instance
207
+ end
208
+
209
+ protected
210
+
211
+ # Initialize instance variables
212
+ def initialize
213
+ @client = nil
214
+ end
215
+
216
+ # Report inputs patch to core
217
+ def send_inputs_patch(sequence)
218
+ if has_default_thread?
219
+ begin
220
+ cmd = { :name => :set_inputs_patch, :patch => sequence.inputs_patch }
221
+ @client.send_command(cmd)
222
+ rescue Exception => e
223
+ fail('Failed to update inputs', Log.format("Failed to apply inputs patch after execution", e, :trace))
224
+ end
225
+ end
226
+ true
227
+ ensure
228
+ stop
229
+ end
230
+
231
+ # Report failure to core
232
+ def report_failure(subject)
233
+ begin
234
+ AuditStub.instance.append_error(subject.failure_title, :category => RightScale::EventCategories::CATEGORY_ERROR) if subject.failure_title
235
+ AuditStub.instance.append_error(subject.failure_message) if subject.failure_message
236
+ rescue Exception => e
237
+ fail('Failed to report failure', Log.format("Failed to report failure after execution", e, :trace))
238
+ ensure
239
+ stop
240
+ end
241
+ end
242
+
243
+ # Print failure message and exit abnormally
244
+ def fail(title, message=nil)
245
+ $stderr.puts title
246
+ $stderr.puts message || title
247
+ if @client
248
+ @client.stop { AuditStub.instance.stop { exit(1) } }
249
+ else
250
+ exit(1)
251
+ end
252
+ end
253
+
254
+ # Stop command client then stop auditor stub then EM
255
+ def stop
256
+ AuditStub.instance.stop do
257
+ @client.stop do |timeout|
258
+ Log.info('[cook] Failed to stop command client cleanly, forcing shutdown...') if timeout
259
+ EM.stop
260
+ end
261
+ end
262
+ end
263
+
264
+ # Provides a blocking request for the given command
265
+ # Can only be called when on EM defer thread
266
+ #
267
+ # === Parameters
268
+ # cmd(Hash):: request to send
269
+ #
270
+ # === Return
271
+ # response(String):: raw response
272
+ #
273
+ # === Raise
274
+ # BlockingError:: If request called when on EM main thread
275
+ def blocking_request(cmd, timeout)
276
+ raise BlockingError, "Blocking request not allowed on EM main thread for command #{cmd.inspect}" if EM.reactor_thread?
277
+ # Use a queue to block and wait for response
278
+ response_queue = Queue.new
279
+ # Need to execute on EM main thread where command client is running
280
+ EM.next_tick { @client.send_command(cmd, false, timeout) { |response| response_queue << response } }
281
+ return response_queue.shift
282
+ end
283
+
284
+ # Load serialized content
285
+ # fail if serialized data is invalid
286
+ #
287
+ # === Parameters
288
+ # data(String):: Serialized content
289
+ # error_message(String):: Error to be logged/audited in case of failure
290
+ # format(Symbol):: Serialization format
291
+ #
292
+ # === Return
293
+ # content(String):: Unserialized content
294
+ def load(data, error_message, format = nil)
295
+ serializer = Serializer.new(format)
296
+ content = nil
297
+ begin
298
+ content = serializer.load(data)
299
+ rescue Exception => e
300
+ fail(error_message, "Failed to load #{serializer.format.to_s} data (#{e}):\n#{data.inspect}")
301
+ end
302
+ content
303
+ end
304
+
305
+ end
306
+ end