right_link 5.9.0

Sign up to get free protection for your applications and to get access to all the features.
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,104 @@
1
+ #
2
+ # Copyright (c) 2009-2011 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
+ module RightScale
24
+
25
+ # Tracks reenroll votes and trigger reenroll as necessary
26
+ class ReenrollManager
27
+
28
+ # Number of votes required to trigger re-enroll
29
+ REENROLL_THRESHOLD = 3
30
+
31
+ # Additional number of seconds that should be spent in offline mode before
32
+ # triggering a re-enroll after vote threshold has been reached
33
+ MAXIMUM_REENROLL_DELAY = 900 # 15 minutes
34
+
35
+ # Delay in seconds until votes count is reset if no more votes occur
36
+ # This value should be more than two hours as this is the period at which
37
+ # votes will get generated in offline mode
38
+ RESET_DELAY = 7200 # 2 hours
39
+
40
+ # Are we currently reenrolling?
41
+ #
42
+ # === Return
43
+ # true:: If we are currently re-enrolling (note: process is going away)
44
+ # false:: Otherwise
45
+ def self.reenrolling?
46
+ @reenrolling
47
+ end
48
+
49
+ # Set reenrolling flag
50
+ #
51
+ # === Return
52
+ # true:: Always return true
53
+ def self.set_reenrolling
54
+ @reenrolling = true
55
+ end
56
+
57
+ # Vote for re-enrolling, if threshold is reached re-enroll
58
+ # If no vote occurs in the next two hours, then reset counter
59
+ #
60
+ # === Return
61
+ # true:: Always return true
62
+ def self.vote
63
+ @total_votes ||= 0
64
+ @reenrolling ||= false
65
+ @total_votes += 1
66
+ @reset_timer.cancel if @reset_timer
67
+ @reset_timer = EM::Timer.new(RESET_DELAY) { reset_votes }
68
+ if @total_votes >= REENROLL_THRESHOLD && !@reenrolling
69
+ delay = rand(MAXIMUM_REENROLL_DELAY)
70
+ Log.info("[re-enroll] Re-enroll threshold reached, shutting down and re-enrolling in #{delay} seconds")
71
+ set_reenrolling
72
+ EM::Timer.new(delay) { reenroll! }
73
+ end
74
+ true
75
+ end
76
+
77
+ # Reset votes count
78
+ #
79
+ # === Return
80
+ # true:: Always return true
81
+ def self.reset_votes
82
+ @total_votes = 0
83
+ @reset_timer = nil
84
+ true
85
+ end
86
+
87
+ # Trigger reenroll
88
+ #
89
+ # === Parameters
90
+ # args(String):: Arguments to be passed through to rs_reenroll
91
+ #
92
+ # === Return
93
+ # true:: If command ran successfully
94
+ # false:: Otherwise
95
+ def self.reenroll!(args=nil)
96
+ cmd = "rs_reenroll #{args}"
97
+ cmd += '&' unless RightScale::Platform.windows?
98
+ AMQP.stop { EM.stop } rescue nil
99
+ system(cmd)
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,181 @@
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
+ # Generate recipes dynamically for RightScripts
28
+ # Usage is:
29
+ # 1. Call 'recipe_from_right_script' for each RightScript that should be converted to a recipe
30
+ # 2. Call 'save' before running Chef, 'recipe_from_right_script' cannot be called after 'save'
31
+ # 3. Use 'repo_dir' to retrieve the Chef cookbook repo path (can be called at any time)
32
+ class RightScriptsCookbook
33
+
34
+ # Name of cookbook containing RightScript recipes
35
+ COOKBOOK_NAME = 'right_scripts_cookbook'
36
+
37
+ # Path to generated cookbook repo
38
+ attr_reader :repo_dir
39
+
40
+ # Wheter 'save' has been called
41
+ attr_reader :saved
42
+
43
+ # Setup temporary directory for cookbook repo containing
44
+ # recipes generated from RightScripts
45
+ def initialize(thread_name)
46
+ @saved = false
47
+ @recipes = {}
48
+ @repo_dir = File.join(AgentConfig.right_scripts_repo_dir, thread_name)
49
+ @cookbook_dir = File.join(@repo_dir, COOKBOOK_NAME)
50
+ @recipes_dir = File.join(@cookbook_dir, 'recipes')
51
+ FileUtils.rm_rf(@cookbook_dir)
52
+ FileUtils.mkdir_p(@recipes_dir)
53
+ end
54
+
55
+ # Add RightScript instantiation to cookbook
56
+ #
57
+ # === Parameters
58
+ # script(RightScale::RightScriptInstantiation):: RightScript to be added
59
+ #
60
+ # === Return
61
+ # recipe(RightScale::RecipeInstantiation):: Recipe that wraps RightScript
62
+ #
63
+ # === Raise
64
+ # (RightScale::Exceptions::Application):: If 'save' has been called
65
+ def recipe_from_right_script(script)
66
+ raise RightScale::Exceptions::Application, 'cannot create recipe after cookbook repo has been saved' if @saved
67
+ path = script_path(script.nickname)
68
+ recipe_name = File.basename(path)
69
+ @recipes[recipe_name] = script.nickname
70
+ recipe_content = <<-EOS
71
+ right_script '#{script.nickname.gsub("'", "\\\\'")}' do
72
+ parameters(node["#{script.nickname}"]["parameters"])
73
+ cache_dir '#{cache_dir(script)}'
74
+ source_file '#{path}'
75
+ display_version '#{script.display_version.to_s.strip.gsub("'", "\\\\'")}'
76
+ end
77
+ EOS
78
+ File.open(path, 'w') { |f| f.puts script.source }
79
+ File.chmod(0744, path)
80
+ recipe_path = "#{path}.rb"
81
+ File.open(recipe_path, 'w') { |f| f.puts recipe_content }
82
+
83
+ recipe = RecipeInstantiation.new("#{COOKBOOK_NAME}::#{recipe_name}",
84
+ { script.nickname => { "parameters" => script.parameters } },
85
+ script.id, script.ready)
86
+
87
+ end
88
+
89
+ # Produce file name for given script nickname
90
+ #
91
+ # === Parameters
92
+ # nickname(String):: Script nick name
93
+ #
94
+ # === Return
95
+ # path(String):: Path to corresponding recipe
96
+ def script_path(nickname)
97
+ base_path = nickname.gsub(/[^0-9a-zA-Z_]/,'_')
98
+ base_path = File.join(@recipes_dir, base_path)
99
+ candidate_path = RightScale::Platform.shell.format_script_file_name(base_path)
100
+ i = 1
101
+ path = candidate_path
102
+ path = candidate_path + (i += 1).to_s while File.exists?(path)
103
+ path
104
+ end
105
+
106
+ # Save cookbook repo
107
+ #
108
+ # === Return
109
+ # true:: Always return true
110
+ def save
111
+ unless empty?
112
+ recipes = @recipes.keys.sort
113
+ metadata_content = <<-EOS
114
+ description "Automatically generated repo, do not modify"
115
+ #{recipes.map { |r| "recipe \"#{COOKBOOK_NAME}::#{r}\", \"RightScript < #{@recipes[r]} >\"" }.join("\n")}
116
+ EOS
117
+ metadata_path = File.join(@cookbook_dir, 'metadata.rb')
118
+ File.open(metadata_path, 'w') { |f| f.puts metadata_content }
119
+ end
120
+ @saved = true
121
+ end
122
+
123
+ # Whether given recipe name corresponds to a converted RightScript
124
+ #
125
+ # === Parameters
126
+ # nickname(String):: Recipe nickname
127
+ #
128
+ # === Return
129
+ # true:: If recipe was created from converting a RightScript
130
+ # false:: Otherwise
131
+ def self.right_script?(nickname)
132
+ # the RightScripts deserialized from core appear to not have the
133
+ # RightScript cookbook in their nickname. actual Chef recipes will have
134
+ # their cookbook name, so assume missing cookbook name means RightScript.
135
+ regex = /^(.*)::/
136
+ match = regex.match(nickname)
137
+ return match.nil? || COOKBOOK_NAME == match[1]
138
+ end
139
+
140
+ # Human friendly title for given recipe instantiation
141
+ #
142
+ # === Parameters
143
+ # recipe(String):: Recipe nickname
144
+ #
145
+ # === Return
146
+ # title(String):: Recipe title to be used in audits
147
+ def self.recipe_title(recipe)
148
+ title = right_script?(recipe) ? 'RightScript' : 'recipe'
149
+ title = "#{title} < #{recipe} >"
150
+ end
151
+
152
+ # Path to cache directory for given script
153
+ #
154
+ # === Parameters
155
+ # script(Object):: script object of some kind (e.g. RightScale::RightScriptInstantiation)
156
+ #
157
+ # === Return
158
+ # path(String):: Path to directory used for attachments and source
159
+ def cache_dir(script)
160
+ # prefix object ID with a text constant to make a legal directory name
161
+ # in case object id is negative (Ubuntu, etc.). this method will be called
162
+ # more than once and must return the same directory each time for a given
163
+ # script instantiation.
164
+ path = File.normalize_path(File.join(AgentConfig.cache_dir, 'right_scripts_content', "rs_attach" + script.object_id.to_s))
165
+
166
+ # convert to native format for ease of scripting in Windows, etc. the
167
+ # normalized path is normal for Ruby but not necessarily for native FS.
168
+ return RightScale::Platform.filesystem.pretty_path(path, true)
169
+ end
170
+
171
+ # Is there no RightScript recipe in repo?
172
+ #
173
+ # === Return
174
+ # true:: If +recipe_from_right_script+ was never called
175
+ # false:: Otherwise
176
+ def empty?
177
+ @recipes.empty?
178
+ end
179
+
180
+ end
181
+ end
@@ -0,0 +1,221 @@
1
+ #
2
+ # Copyright (c) 2011 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
+ module RightScale
24
+
25
+ # Mixin for defining a common interface for a shutdown request class and its
26
+ # proxy class.
27
+ module ShutdownRequestMixin
28
+
29
+ # initial kind; no shutdown requested.
30
+ CONTINUE = 'continue'
31
+
32
+ # requested a reboot.
33
+ REBOOT = 'reboot'
34
+
35
+ # requested a stop (keep boot volume after shutdown).
36
+ STOP = 'stop'
37
+
38
+ # requested a terminate (discard boot volume after shutdown).
39
+ TERMINATE = 'terminate'
40
+
41
+ # levels.
42
+ LEVELS = [CONTINUE, REBOOT, STOP, TERMINATE]
43
+
44
+ # exceptions.
45
+ class NotInitialized < Exception; end
46
+ class InvalidLevel < Exception; end
47
+
48
+
49
+ # true if no shutdown was requested, false if shutdown was requested.
50
+ def continue?; CONTINUE == @level; end
51
+
52
+ # true if any requested shutdown will interrupt sequence of running scripts
53
+ # immediately (current script is allowed to complete).
54
+ # false to defer shutdown until all outstanding scripts have run.
55
+ def immediately?; @immediately; end
56
+ def immediately!
57
+ raise InvalidLevel.new("Immediately is unexpected for current shutdown state") if continue?
58
+ @immediately = true
59
+ end
60
+
61
+ # Shutdown request level.
62
+ def level; @level; end
63
+ def level=(value)
64
+ value = value.to_s
65
+ raise InvalidLevel.new("Invalid shutdown level: #{value.inspect}") unless LEVELS.include?(value)
66
+
67
+ # strictly escalate to higher level and ignore lower level requests.
68
+ @level = value if LEVELS.index(value) > LEVELS.index(@level)
69
+ end
70
+
71
+ # Stringizer.
72
+ def to_s
73
+ # note that printing 'deferred' would seem strange at the time when the
74
+ # deferred shutdown is actually being processed, so only say immediately.
75
+ immediacy = if @immediately; ' immediately'; else; ''; end
76
+ return "#{@level}#{immediacy}"
77
+ end
78
+
79
+ protected
80
+
81
+ def initialize
82
+ @level = CONTINUE
83
+ @immediately = false
84
+ end
85
+
86
+ end
87
+
88
+ # Represents outstanding request(s) for reboot, stop or terminate instance.
89
+ # Requests are cumulative and implicitly non-decreasing in level (e.g. reboot
90
+ # never superceeds terminate).
91
+ class ShutdownRequest
92
+
93
+ include ShutdownRequestMixin
94
+
95
+ # Class initializer.
96
+ #
97
+ # === Parameters
98
+ # scheduler(InstanceScheduler):: scheduler for shutdown requests
99
+ #
100
+ # === Return
101
+ # always true
102
+ def self.init(scheduler)
103
+ @@instance = ShutdownRequest.new
104
+ @@scheduler = scheduler
105
+ true
106
+ end
107
+
108
+ # Factory method
109
+ #
110
+ # === Return
111
+ # (ShutdownRequest):: the singleton for this class
112
+ def self.instance
113
+ raise NotInitialized.new("ShutdownRequest.init has not been called") unless defined?(@@instance)
114
+ return @@instance
115
+ end
116
+
117
+ # Submits a new shutdown request state which may be superceded by a
118
+ # previous, higher-priority shutdown level.
119
+ #
120
+ # === Parameters
121
+ # request[:level](String):: shutdown level
122
+ # request[:immediately](Boolean):: shutdown immediacy or nil
123
+ #
124
+ # === Returns
125
+ # result(ShutdownRequest):: the updated instance
126
+ def self.submit(request)
127
+ # RightNet protocols use kind instead of level, so be a little flexible.
128
+ result = instance
129
+ result.level = request[:kind] || request[:level]
130
+ result.immediately! if request[:immediately]
131
+ @@scheduler.schedule_shutdown unless result.continue?
132
+ return result
133
+ end
134
+
135
+ # Processes shutdown requests by communicating the need to shutdown an
136
+ # instance with the core agent, if necessary.
137
+ #
138
+ # === Parameters
139
+ # errback(Proc):: error handler or nil
140
+ # audit(Audit):: audit for shutdown action, if needed, or nil.
141
+ #
142
+ # === Block
143
+ # block(Proc):: continuation block for successful handling of shutdown or nil
144
+ #
145
+ # === Return
146
+ # always true
147
+ def process(errback = nil, audit = nil, &block)
148
+ # yield if not shutting down (continuing) or if already requested shutdown.
149
+ if continue? || @shutdown_scheduled
150
+ block.call if block
151
+ return true
152
+ end
153
+
154
+ # ensure we have an audit, creating a temporary audit if necessary.
155
+ sender = Sender.instance
156
+ agent_identity = sender.identity
157
+ if audit
158
+ case @level
159
+ when REBOOT, STOP, TERMINATE
160
+ operation = "/forwarder/shutdown"
161
+ payload = {:agent_identity => agent_identity, :kind => @level}
162
+ else
163
+ raise InvalidLevel.new("Unexpected shutdown level: #{@level.inspect}")
164
+ end
165
+
166
+ # request shutdown (kind indicated by operation and/or payload).
167
+ audit.append_info("Shutdown requested: #{self}")
168
+ sender.send_persistent_request(operation, payload) do |r|
169
+ res = OperationResult.from_results(r)
170
+ if res.success?
171
+ @shutdown_scheduled = true
172
+ block.call if block
173
+ else
174
+ fail(errback, audit, "Failed to shutdown instance", res)
175
+ end
176
+ end
177
+ else
178
+ AuditProxy.create(agent_identity, "Shutdown requested: #{self}") do |new_audit|
179
+ process(errback, new_audit, &block)
180
+ end
181
+ end
182
+ true
183
+ rescue Exception => e
184
+ fail(errback, audit, e)
185
+ end
186
+
187
+ protected
188
+
189
+ def initialize
190
+ super
191
+ @shutdown_scheduled = false
192
+ end
193
+
194
+ # Handles any shutdown failure.
195
+ #
196
+ # === Parameters
197
+ # audit(Audit):: Audit or nil
198
+ # errback(Proc):: error handler or nil
199
+ # msg(String):: Error message that will be audited and logged
200
+ # res(RightScale::OperationResult):: Operation result with additional information
201
+ #
202
+ # === Return
203
+ # always true
204
+ def fail(errback, audit, msg, res = nil)
205
+ if msg.kind_of?(Exception)
206
+ e = msg
207
+ detailed = Log.format("Could not process shutdown state #{self}", e, :trace)
208
+ msg = e.message
209
+ else
210
+ detailed = nil
211
+ end
212
+ msg += ": #{res.content}" if res && res.content
213
+ audit.append_error(msg, :category => RightScale::EventCategories::CATEGORY_ERROR) if audit
214
+ Log.error(detailed) if detailed
215
+ errback.call if errback
216
+ true
217
+ end
218
+
219
+ end # ShutdownRequest
220
+
221
+ end # RightScale
@@ -0,0 +1,189 @@
1
+ #
2
+ # Copyright (c) 2011 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
+ module RightScale
24
+
25
+ class SingleThreadBundleQueue < BundleQueue
26
+
27
+ attr_reader :thread_name
28
+
29
+ # Set continuation block to be called after 'close' is called
30
+ #
31
+ # === Block
32
+ # continuation block
33
+ def initialize(thread_name = ::RightScale::AgentConfig.default_thread_name, &continuation)
34
+ super(&continuation)
35
+ @active = false
36
+ @thread = nil
37
+ @thread_name = thread_name
38
+ @pid = nil
39
+ @mutex = Mutex.new
40
+ @queue = Queue.new
41
+ @sequence_finished = ConditionVariable.new
42
+ end
43
+
44
+ # Determines if queue is active
45
+ #
46
+ # === Return
47
+ # active(Boolean):: true if queue is active
48
+ def active?
49
+ active = false
50
+ @mutex.synchronize { active = @active }
51
+ return active
52
+ end
53
+
54
+ # Activate queue for execution, idempotent
55
+ # Any pending bundle will be run sequentially in order
56
+ #
57
+ # === Return
58
+ # true:: Always return true
59
+ def activate
60
+ @mutex.synchronize do
61
+ unless @active
62
+ @thread = Thread.new { run }
63
+ @active = true
64
+ end
65
+ end
66
+ true
67
+ end
68
+
69
+ # Determines if queue is busy
70
+ #
71
+ # === Return
72
+ # active(Boolean):: true if queue is busy
73
+ def busy?
74
+ busy = false
75
+ @mutex.synchronize { busy = !!@busy }
76
+ busy
77
+ end
78
+
79
+ # Push new context to bundle queue and run next bundle
80
+ #
81
+ # === Return
82
+ # true:: Always return true
83
+ def push(context)
84
+ @queue << context
85
+ true
86
+ end
87
+
88
+ # Clear queue content
89
+ #
90
+ # === Return
91
+ # true:: Always return true
92
+ def clear
93
+ @queue.clear
94
+ true
95
+ end
96
+
97
+ # Close queue so that further call to 'push' will be ignored
98
+ #
99
+ # === Return
100
+ # true:: Always return true
101
+ def close
102
+ push(FINAL_BUNDLE)
103
+ end
104
+
105
+ protected
106
+
107
+ # Run next bundle in the queue if active
108
+ # If bundle is FINAL_BUNDLE then call continuation block and deactivate
109
+ #
110
+ # === Return
111
+ # true:: Always return true
112
+ def run
113
+ loop do
114
+ context = @queue.shift
115
+ if context == FINAL_BUNDLE
116
+ break
117
+ elsif context == SHUTDOWN_BUNDLE
118
+ # process shutdown request.
119
+ ShutdownRequest.instance.process
120
+ # continue in queue in the expectation that the decommission bundle will
121
+ # shutdown the instance and its agent normally.
122
+ elsif !context.decommission? && ShutdownRequest.instance.immediately?
123
+ # immediate shutdown pre-empts any futher attempts to run operational
124
+ # scripts but still allows the decommission bundle to run.
125
+ context.audit.update_status("Skipped bundle due to immediate shutdown of #{@thread_name} thread: #{context.payload}")
126
+ # proceed ignoring bundles until final or shutdown are encountered.
127
+ else
128
+ sequence = create_sequence(context)
129
+ sequence.callback { audit_status(sequence) }
130
+ sequence.errback { audit_status(sequence) }
131
+
132
+ # wait until sequence is finished using a ruby mutex conditional.
133
+ # need to synchronize before run to ensure we are waiting before any
134
+ # immediate signalling occurs (under test conditions, etc.).
135
+ @mutex.synchronize do
136
+ @busy = true
137
+ sequence.run
138
+ @sequence_finished.wait(@mutex)
139
+ @pid = nil
140
+ @busy = false
141
+ end
142
+ end
143
+ end
144
+ true
145
+ rescue Exception => e
146
+ Log.error(Log.format("SingleThreadBundleQueue.run failed for #{@thread_name} thread", e, :trace))
147
+ ensure
148
+ # invoke continuation (off of this thread which is going away).
149
+ @mutex.synchronize { @active = false }
150
+ run_continuation
151
+ @thread = nil
152
+ end
153
+
154
+ # Factory method for a new sequence.
155
+ #
156
+ # context(RightScale::OperationContext)
157
+ def create_sequence(context)
158
+ pid_callback = lambda do |sequence|
159
+ # TODO preserve cook PIDs per thread in InstanceState and recover
160
+ # orphaned cook in case of agent crash.
161
+ @mutex.synchronize { @pid = sequence.pid }
162
+ end
163
+ return RightScale::ExecutableSequenceProxy.new(context, :pid_callback => pid_callback )
164
+ end
165
+
166
+ # Audit executable sequence status after it ran
167
+ #
168
+ # === Parameters
169
+ # sequence(RightScale::ExecutableSequence):: finished sequence being audited
170
+ #
171
+ # === Return
172
+ # true:: Always return true
173
+ def audit_status(sequence)
174
+ context = sequence.context
175
+ title = context.decommission? ? 'decommission ' : ''
176
+ title += context.succeeded ? 'completed' : 'failed'
177
+ context.audit.update_status("#{title}: #{context.payload}")
178
+ true
179
+ rescue Exception => e
180
+ Log.error(Log.format("SingleThreadBundleQueue.audit_status failed for #{@thread_name} thread", e, :trace))
181
+ ensure
182
+ # release queue thread to wait on next bundle in queue. we must ensure
183
+ # that we are not currently on the queue thread so next-tick the signal.
184
+ EM.next_tick { @mutex.synchronize { @sequence_finished.signal } }
185
+ end
186
+
187
+ end
188
+
189
+ end