rs-mule 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZDA0NmVlMGNmZWE4YTNjNmQ1NGZmM2ZiNTY0MGFjNjBjMzlmZmYyMg==
4
+ MDdkYTJkMjE5NjQ1ZGY5ZjY3ZDkyMjA5MTZhM2E4ODg4NTA4ZjZiMg==
5
5
  data.tar.gz: !binary |-
6
- OTZlMTEwZjBlNzJjOWI2MzQ4MGY2ZWFjM2E4MGM2N2NhOWY0MDc0Yw==
6
+ OGExMjcwZGFmNmQxYTJlYTZiYjkzMjRmZTIyN2U5ZmVjNWJkNWNhNQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MjQ4NTViOTgxZDQ2MmQ2YzJkMWE1NWJiZjgwNjBmNmFlMTNkMWZiMzExYmYz
10
- OGQ5NjRhNjc0MWZiNjM3YjljNzk5YmEwOGI4MWRkOWVkNTg5NzBmY2E4M2E2
11
- MDg3NzAyN2QyN2Y5NDNmNWMzZDFiMmY3MThhYzUwNTFjZmI3MWU=
9
+ ZmQ5MDNlMzYxYTc2OTc5NDVkMTU0NmVhOTRjN2IyNTkyNDVhMDliOGFhYzQ1
10
+ ZWJkMjE2NzQ1OGQ2MWJhMTg2NTJjM2M4MzU5Yzc0YzJiNmI1OTEyYTg2MGRi
11
+ NmFjZGI2ZjkzMzY4ZTYyNWYyNDgxZmI1NGIxZGM5NDYxMjAwZDI=
12
12
  data.tar.gz: !binary |-
13
- NjY0NDgzMmI3ZGU4NjMyNTUyOTcxMjcxZDRjMDVjMmZjNDIxYzMwNjhjODkx
14
- YjA1YTEzOGZmZDFhNmYwMGNkZGQ1OGI4MDkwY2U1Y2UyYWMzZmZjYWNkZGRm
15
- NWZkN2EyNWMyYWFiZjUyZjAzNmMzODAyYWMyZDhkNWU5ZTk5ZmE=
13
+ MDI5NGZmYjllZTc4YmM3NTFmNzlmYTUyODRmZTFlYjNlOTRkMTNlMGZjNjll
14
+ Yzg3MWRmZDU4ZTA1MDhjZTMzZDljNzlhYWUyM2QxYjFlMGI5Y2ZlZGJiYmUz
15
+ MDkzYmI5OThkNWExNWQ2MjFlZWNiYjJmNzFlOTQ4N2Q1YWJmOGE=
data/README.md CHANGED
@@ -132,6 +132,47 @@ Chef Recipe:
132
132
  rs-mule run_executable "cookbook::recipe" --executable-type=recipe_name --tags=tag1
133
133
  ```
134
134
 
135
+ ##### Script Inputs
136
+ Of course you might want to pass some inputs to the script or recipe that you're
137
+ executing. Shockingly enough, you can!
138
+
139
+ Passing INPUT_ONE and INPUT_TWO to a script RightScript:
140
+ ```
141
+ rs-mule run_executable "Some RightScript Name" --tags=tag1 --inputs=INPUT_ONE:text:foo INPUT_TWO:text:bar
142
+ ```
143
+
144
+ Passing some/chef/attribute1 and some/chef/attribute2 to a recipe:
145
+ ```
146
+ rs-mule run_executable "cookbook::recipe" --tags=tag1 --inputs='some/chef/attribute:text:foo' 'some/chef/attribute2:text:bar'
147
+ ```
148
+
149
+ Suppose you want to set those inputs so that they stick around after you've run
150
+ the executable. You can do that too. You just need to specify where you want
151
+ the inputs to be set.
152
+
153
+ Saving the inputs on the current instance:
154
+ ```
155
+ rs-mule run_executable "Some RightScript Name" --tags=tag1 --inputs=INPUT_ONE:text:foo INPUT_TWO:text:bar --update_inputs=current_instance
156
+ ```
157
+
158
+ Saving the inputs on the next instance:
159
+ ```
160
+ rs-mule run_executable "Some RightScript Name" --tags=tag1 --inputs=INPUT_ONE:text:foo INPUT_TWO:text:bar --update_inputs=next_instance
161
+ ```
162
+
163
+ Saving the inputs on the deployment containing the instance:
164
+ ```
165
+ rs-mule run_executable "Some RightScript Name" --tags=tag1 --inputs=INPUT_ONE:text:foo INPUT_TWO:text:bar --update_inputs=deployment
166
+ ```
167
+
168
+ You can save the inputs on any mix of them as well, since the update_inputs
169
+ option is an array.
170
+
171
+ Saving the inputs on EVERYTHING:
172
+ ```
173
+ rs-mule run_executable "Some RightScript Name" --tags=tag1 --inputs=INPUT_ONE:text:foo INPUT_TWO:text:bar --update_inputs=current_instance next_instance deployment
174
+ ```
175
+
135
176
  ## Library
136
177
  The library does all this cool stuff, and I'll document it, I swear...
137
178
 
@@ -0,0 +1,23 @@
1
+ # Copyright (c) 2014 Ryan Geyer
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ # this software and associated documentation files (the "Software"), to deal in
5
+ # the Software without restriction, including without limitation the rights to
6
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ # the Software, and to permit persons to whom the Software is furnished to do so,
8
+ # subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ require 'thor'
21
+ require 'right_api_client'
22
+ glob_path = File.expand_path(File.join(File.dirname(__FILE__), 'rs-mule')) + '/**/*.rb'
23
+ Dir.glob(glob_path, &method(:require))
@@ -0,0 +1,71 @@
1
+ # Copyright (c) 2014 Ryan Geyer
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ # this software and associated documentation files (the "Software"), to deal in
5
+ # the Software without restriction, including without limitation the rights to
6
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ # the Software, and to permit persons to whom the Software is furnished to do so,
8
+ # subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ require 'yaml'
21
+
22
+ module RsMule
23
+ class Cli < Thor
24
+ no_commands {
25
+ def get_right_api_client()
26
+ client_auth_params = {}
27
+ thor_shell = Thor::Shell::Color.new
28
+ unless @options.keys.include?('rs_auth_file') | @options.keys.include?('rs_auth_hash')
29
+ message = <<EOF
30
+ You must supply right_api_client authentication details as either a hash or
31
+ a yaml authentication file!
32
+ EOF
33
+ thor_shell.say(thor_shell.set_color(message, :red))
34
+ exit 1
35
+ end
36
+ if @options[:rs_auth_file]
37
+ client_auth_params = YAML.load_file(@options[:rs_auth_file])
38
+ end
39
+
40
+ if @options[:rs_auth_hash]
41
+ client_auth_params = options[:rs_auth_hash]
42
+ end
43
+
44
+ RightApi::Client.new(client_auth_params)
45
+ end
46
+ }
47
+
48
+ desc "run_executable", "Runs a specified recipe or RightScript on instances targeted by tag"
49
+ option :rs_auth_hash, :type => :hash, :desc => "A hash of right_api_client auth parameters in the form (email:foo@bar.baz password:password account_id:12345)"
50
+ option :rs_auth_file, :desc => "A yaml file containing right_api_client auth parameters to use for authentication"
51
+ option :tags, :type => :array, :required => true
52
+ option :tag_match_strategy, :desc => "If multiple tags are specified, this will determine how they are matched. When set to \"all\" instances with all tags will be matched. When set to \"any\" instances with any of the provided tags will be matched. Defaults to \"all\""
53
+ option :executable_type, :desc => "What value is being provided for the executable parameter. One of [auto|right_script_name|right_script_href|recipe_name]"
54
+ option :right_script_revision, :desc => "When a RightScript name is provided, this can be used to specify which revision to use. If not provided the latest revision will be used."
55
+ option :inputs, :type => :hash, :desc => "A hash where the keys are the name of an input and the value is the desired value for that input. Uses Inputs 2.0 semantics."
56
+ option :update_inputs, :type => :array, :desc => "An array of values indicating which objects should be updated with the inputs supplied. Can be empty in which case the inputs will be used only for this execution. Acceptable values are [\"current_instance\",\"next_instance\",\"deployment\"]"
57
+ def run_executable(executable)
58
+ # Cover our bases with symbolized keys as well
59
+ new_options = {}
60
+ @options.each do |k,v|
61
+ new_options[k.to_sym] = v
62
+ new_options[k] = v
63
+ end
64
+
65
+ client = get_right_api_client
66
+
67
+ mule = RsMule::RunExecutable.new(client)
68
+ mule.run_executable(new_options[:tags], executable, new_options)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2014 Ryan Geyer
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ # this software and associated documentation files (the "Software"), to deal in
5
+ # the Software without restriction, including without limitation the rights to
6
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ # the Software, and to permit persons to whom the Software is furnished to do so,
8
+ # subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ module RsMule
21
+ module Exception
22
+ class RightScriptNotFound < ::Exception
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,186 @@
1
+ # Copyright (c) 2014 Ryan Geyer
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ # this software and associated documentation files (the "Software"), to deal in
5
+ # the Software without restriction, including without limitation the rights to
6
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ # the Software, and to permit persons to whom the Software is furnished to do so,
8
+ # subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ module RsMule
21
+ class RunExecutable
22
+
23
+ attr_accessor :right_api_client
24
+
25
+ # Initializes a new RunExecutable
26
+ #
27
+ # @param [RightApi::Client] right_api_client An instantiated and authenticated
28
+ # RightApi::Client instance which will be used for making the request(s)
29
+ def initialize(right_api_client)
30
+ @right_api_client = right_api_client
31
+ end
32
+
33
+ # Runs a RightScript or Chef Recipe on all instances which have all of the
34
+ # specified tags.
35
+ #
36
+ # @param [String|Array<String>] tags An array of tags. If a string is
37
+ # supplied it will be converted to an array of Strings.
38
+ # @param [String] executable RightScript name or href, or the name of a
39
+ # recipe. This method will attempt to auto detect which it is, or you
40
+ # can be friendly and provide a hint using the executable_type option.
41
+ # @param [Hash] options A list of options where the possible values are
42
+ # - executable_type [String] One of ["auto","right_script_name","right_script_href","recipe_name"]. When set
43
+ # to "auto" we will attempt to determine if an executable is a recipe,
44
+ # a RightScript name, or a RightScript href. Defaults to "auto"
45
+ # - right_script_revision [String] When a RightScript name or href is specified,
46
+ # this can be used to declare the revision to use. Can be a specific
47
+ # revision number or "latest". Defaults to "latest"
48
+ # - tag_match_strategy [String] If multiple tags are specified, this will
49
+ # determine how they are matched. When set to "all" instances with all
50
+ # tags will be matched. When set to "any" instances with any of the
51
+ # provided tags will be matched. Defaults to "all"
52
+ # - inputs [Hash] A hash where the keys are the name of an input and the
53
+ # value is the desired value for that input. Uses Inputs 2.0[http://reference.rightscale.com/api1.5/resources/ResourceInputs.html#multi_update]
54
+ # semantics.
55
+ # - update_inputs [Array<String>] An array of values indicating which
56
+ # objects should be updated with the inputs supplied. Can be empty in
57
+ # which case the inputs will be used only for this execution. Acceptable
58
+ # values are ["current_instance","next_instance","deployment"]
59
+ # @raise [RightScriptNotFound] If the specified RightScript lineage does
60
+ # not exist, or the specified revision is not available.
61
+ def run_executable(tags, executable, options={})
62
+ options = {
63
+ :executable_type => "auto",
64
+ :right_script_revision => "latest",
65
+ :tag_match_strategy => "all",
66
+ :inputs => {},
67
+ :update_inputs => []
68
+ }.merge(options)
69
+ execute_params = {}
70
+ tags = [tags] unless tags.is_a?(Array)
71
+ options[:update_inputs] = [options[:update_inputs]] unless options[:update_inputs].is_a?(Array)
72
+
73
+ case options[:executable_type]
74
+ when "right_script_href"
75
+ execute_params[:right_script_href] = executable
76
+ when "right_script_name"
77
+ scripts = find_right_script_lineage_by_name(executable)
78
+ execute_params[:right_script_href] = right_script_revision_from_lineage(scripts, options[:right_script_revision]).href
79
+ when "recipe_name"
80
+ execute_params[:recipe_name] = executable
81
+ when "auto"
82
+ is_recipe = executable =~ /.*::.*/
83
+ is_href = executable =~ /^\/api\/right_scripts\/[a-zA-Z0-9]*/
84
+ if is_recipe
85
+ execute_params[:recipe_name] = executable
86
+ else
87
+ if is_href
88
+ execute_params[:right_script_href] = executable
89
+ else
90
+ scripts = find_right_script_lineage_by_name(executable)
91
+ execute_params[:right_script_href] = right_script_revision_from_lineage(scripts, options[:right_script_revision]).href
92
+ end
93
+ end
94
+ else
95
+ raise ArgumentError.new("Unknown executable_type (#{options[:executable_type]})")
96
+ end
97
+
98
+ if options[:inputs].length > 0
99
+ execute_params[:inputs] = options[:inputs]
100
+ end
101
+
102
+ resources_by_tag = @right_api_client.tags.by_tag(
103
+ :resource_type => "instances",
104
+ :tags => tags,
105
+ :match_all => options[:tag_match_strategy] == "all" ? "true" : "false"
106
+ )
107
+
108
+ resources_by_tag.each do |res|
109
+ instance = @right_api_client.resource(res.links.first["href"])
110
+ instance.run_executable(execute_params)
111
+ options[:update_inputs].each do |update_type|
112
+ update_inputs(instance, options[:inputs], update_type)
113
+ end
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ # Fetches the entire lineage (all revisions) of a RightScript when provided
120
+ # with it's name
121
+ #
122
+ # @param [String] name The name (or partial name) of the RightScript
123
+ # @return [Array<RightApi::Resource>] An array of RightApi::Resource objects
124
+ # of media type RightScript[http://reference.rightscale.com/api1.5/media_types/MediaTypeRightScript.html]
125
+ # @raise [RightScriptNotFound] If a lineage of RightScripts with the specified
126
+ # name is not found.
127
+ def find_right_script_lineage_by_name(name)
128
+ lineage = @right_api_client.right_scripts(:filter => ["name==#{name}"]).index
129
+ if lineage.length == 0
130
+ raise Exception::RightScriptNotFound.new("No RightScripts with the name (#{name}) were found.")
131
+ end
132
+ lineage
133
+ end
134
+
135
+ # Gets the specified revision of a RightScript from it's lineage
136
+ #
137
+ # @param [Array<RightApi::Resource>] An array of RightApi::Resource objects
138
+ # of media type RightScript[http://reference.rightscale.com/api1.5/media_types/MediaTypeRightScript.html]
139
+ # @param [String] An optional parameter for the desired lineage. When set
140
+ # to "latest" it will get the highest number committed revision. Specify
141
+ # 0 for the head revision.
142
+ # @raise [RightScriptNotFound] If the specified revision is not in the lineage
143
+ def right_script_revision_from_lineage(lineage, revision="latest")
144
+ right_script = nil
145
+ if revision == "latest"
146
+ latest_script = lineage.max_by{|rs| rs.revision}
147
+ right_script = latest_script
148
+ else
149
+ desired_script = lineage.select{|rs| rs.revision == revision}
150
+ if desired_script.length == 0
151
+ raise Exception::RightScriptNotFound.new("RightScript revision (#{revision}) was not found. Available revisions are (#{lineage.map{|rs| rs.revision}})")
152
+ end
153
+ right_script = desired_script.first
154
+ end
155
+ right_script
156
+ end
157
+
158
+ # Updates inputs on one of the objects related to the specified instance.
159
+ # This deliberately lacks error handling. If you attempt to set the input on
160
+ # the deployment of the matching instance(s) and there isn't a deployment,
161
+ # you'll get the exception.
162
+ #
163
+ # TODO: Maybe handle the error so that other instances in a matched set can
164
+ # have a chance to succeed.
165
+ #
166
+ # @param [RightApi::Resource] The RightApi::Resource of media type
167
+ # Instance[http://reference.rightscale.com/api1.5/media_types/MediaTypeInstance.html]
168
+ # which will be used to find related objects, or which will get it's inputs
169
+ # updated.
170
+ # @param [Hash] inputs A hash where the keys are the name of an input and the
171
+ # value is the desired value for that input. Uses Inputs 2.0[http://reference.rightscale.com/api1.5/resources/ResourceInputs.html#multi_update]
172
+ # semantics.
173
+ # @param [String] update_type Which object should be updated with the inputs
174
+ # supplied. Acceptable values are ["current_instance","next_instance","deployment"]
175
+ def update_inputs(instance, inputs, update_type)
176
+ case update_type
177
+ when "current_instance"
178
+ instance.inputs.multi_update(:inputs => inputs)
179
+ when "next_instance"
180
+ instance.parent.show.next_instance.show.inputs.multi_update(:inputs => inputs)
181
+ when "deployment"
182
+ instance.deployment.show.inputs.multi_update(:inputs => inputs)
183
+ end
184
+ end
185
+ end
186
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rs-mule
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan J. Geyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-12 00:00:00.000000000 Z
11
+ date: 2014-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: right_api_client
@@ -48,6 +48,10 @@ files:
48
48
  - LICENSE
49
49
  - README.md
50
50
  - bin/rs-mule
51
+ - lib/rs-mule.rb
52
+ - lib/rs-mule/cli.rb
53
+ - lib/rs-mule/exception/right_script_not_found.rb
54
+ - lib/rs-mule/run_executable.rb
51
55
  homepage: https://github.com/rgeyer/rs-mule
52
56
  licenses:
53
57
  - MIT