resat 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ = Synopsis
2
+
3
+ This example uses the RightScale REST API:
4
+ http://wiki.rightscale.com/2._References/01-RightScale/03-RightScale_API
5
+
6
+ This example will list all your servers, create a new server using the
7
+ 'Rails all-in-one' server template and delete it.
8
+
9
+ See the main README.rdoc for instructions on how to setup resat prior to
10
+ running the examples.
11
+
12
+ = How to
13
+
14
+ * Run:
15
+
16
+ $ resat scenarios -d user:<your RightScale username> -d pass:<your RightScale password> -d acct:<your RightScale account number>
17
+
18
+ * See:
19
+
20
+ $ cat output.yml
21
+
22
+ * See more:
23
+
24
+ $ vi /tmp/resat.log
25
+
26
+ or
27
+
28
+ $ vi resat.log
29
+
30
+ if <tt>/tmp</tt> does not exist
31
+
32
+ = Additional Examples
33
+
34
+ The <i>run_server</i> example in the <i>additional</i> folder will create and
35
+ launch a server in the <i>default</i> deployment and wait until it's
36
+ operational before running an operational script on it. It will then stop and
37
+ delete it. See the file <tt>additional/run_server.yml</tt>
38
+ (http://github.com/raphael/resat/blob/master/examples/rightscale/additional/run_server.yml)
39
+ for additional information.
@@ -0,0 +1,75 @@
1
+ # This scenario will launch an All-in-on Rails server, wait for it to become
2
+ # operational, run a RightScript (Mongrels restart) and then stop the server,
3
+ # wait for it to be stopped and delete it.
4
+ #
5
+ # Note: This scenario reuses the 'create_server' scenario to create the
6
+ # Rails All-in-one server.
7
+ #
8
+ name: Launch rails servers and run RightScript
9
+ includes:
10
+ - ../scenarios/create_server
11
+ steps:
12
+ - request: # Step 1. Start server
13
+ resource: servers
14
+ id: $server_id
15
+ custom:
16
+ name: start
17
+ type: post
18
+
19
+ - request: # Step 2. Retrieve Mongrels restart RightScript
20
+ operation: index
21
+ resource: right_scripts
22
+ filters:
23
+ - name: Get Rails server template
24
+ target: body
25
+ extractors:
26
+ - field: right-scripts/right-script[name='RB mongrel_cluster (re)start v1']/href
27
+ variable: right_script_id
28
+
29
+ - request: # Step 3. Wait for server to become operational
30
+ resource: servers
31
+ id: $server_id
32
+ operation: show
33
+ guards:
34
+ - name: Wait for operational state
35
+ target: body
36
+ field: server/state
37
+ pattern: "operational"
38
+ period: 10
39
+ timeout: 900
40
+
41
+ - request: # Step 4. Restart Mongrels
42
+ resource: servers
43
+ id: $server_id
44
+ custom:
45
+ name: run_script
46
+ type: post
47
+ params:
48
+ - name: right_script
49
+ value: $right_script_id
50
+
51
+ - request: # Step 5. Stop server
52
+ resource: servers
53
+ id: $server_id
54
+ custom:
55
+ name: stop
56
+ type: post
57
+
58
+ - request: # Step 6. Wait for server to become stopped
59
+ resource: servers
60
+ id: $server_id
61
+ operation: show
62
+ guards:
63
+ - name: Wait for stop state
64
+ target: body
65
+ field: server/state
66
+ pattern: "stopped"
67
+ period: 10
68
+ timeout: 900
69
+
70
+ - request: # Step 7. Now delete the server
71
+ operation: destroy
72
+ resource: servers
73
+ id: $server_id
74
+
75
+
@@ -0,0 +1,34 @@
1
+ # Hostname used for API calls
2
+ host: moo.rightscale.com
3
+
4
+ # Port
5
+ # port: 443
6
+
7
+ # Common base URL to all API calls
8
+ base_url: api/acct/$acct
9
+
10
+ # Use HTTPS?
11
+ use_ssl: yes
12
+
13
+ # Basic auth username if any
14
+ username: $user
15
+
16
+ # Basic auth password if any
17
+ password: $pass
18
+
19
+ # Common request headers for all API calls
20
+ headers:
21
+ - name: X-API-VERSION
22
+ value: '1.0'
23
+
24
+ # Common parameters for all API calls
25
+ params:
26
+
27
+ # Global variables
28
+ variables:
29
+
30
+ # Input file
31
+ #input: variables.yml
32
+
33
+ # Output file
34
+ #output: variables.yml
@@ -0,0 +1,48 @@
1
+ # Create new Rails all-in-one server in default deployment
2
+ name: Create Rails Server
3
+ steps:
4
+ - request: # Step 1. Retrieve default deployment
5
+ operation: index
6
+ resource: deployments
7
+ valid_codes:
8
+ - 200
9
+ filters:
10
+ - name: Get default deployment
11
+ target: body
12
+ extractors:
13
+ - field: deployments/deployment/href
14
+ variable: deployment
15
+
16
+ - request: # Step 2. Retrieve Rails template
17
+ operation: index
18
+ resource: server_templates
19
+ filters:
20
+ - name: Get Rails server template
21
+ target: body
22
+ extractors:
23
+ - field: server-templates/ec2-server-template[nickname='Rails all-in-one-developer v8']/href
24
+ variable: server_template
25
+
26
+ - request: # Step 3. Create server
27
+ operation: create
28
+ resource: servers
29
+ valid_codes:
30
+ - 201
31
+ params:
32
+ - name: server[nickname]
33
+ value: 'Resat Rails Server'
34
+ - name: server[server_template_href]
35
+ value: $server_template
36
+ - name: server[deployment_href]
37
+ value: $deployment
38
+ filters:
39
+ - name: validate server response
40
+ target: body
41
+ is_empty: true
42
+ - name: Get server id
43
+ target: header
44
+ extractors:
45
+ - field: location
46
+ pattern: '.*\/(\d+)$'
47
+ variable: server_id
48
+ export: true # Export id to other scenarios
@@ -0,0 +1,13 @@
1
+ # Delete previously created server
2
+ name: Delete Rails Server
3
+ steps:
4
+ - request:
5
+ operation: destroy
6
+ resource: servers
7
+ id: $server_id
8
+ valid_codes:
9
+ - 200
10
+ filters:
11
+ - name: validate destroy response
12
+ target: body
13
+ is_empty: true
@@ -0,0 +1,9 @@
1
+ # List servers in default deployment
2
+ name: List servers
3
+ steps:
4
+ - request:
5
+ operation: index
6
+ resource: servers
7
+ valid_codes:
8
+ - 200
9
+
@@ -0,0 +1,50 @@
1
+ = Synopsis
2
+
3
+ This example uses the Twitter REST API:
4
+
5
+ http://apiwiki.twitter.com/REST+API+Documentation
6
+
7
+ <b>Please note</b>: This example will send a tweet on your behalf with the text:
8
+
9
+ Checking out resat (http://tinyurl.com/dg8gf9)
10
+
11
+ by default. Override the default text in the <tt>config/resat.yaml</tt> file or
12
+ via the command line:
13
+
14
+ $ resat scenarios -d tweet:'My custom tweet' -d user:... -d pass:...
15
+
16
+ See the main README.rdoc for instructions on how to setup resat prior to
17
+ running the examples.
18
+
19
+ = How to
20
+
21
+ * Run:
22
+
23
+ $ resat scenarios -d user:<your twitter username> -d pass:<your twitter password>
24
+
25
+ * See:
26
+
27
+ $ cat output.yml
28
+
29
+ * See more:
30
+
31
+ $ vi /tmp/resat.log
32
+
33
+ or
34
+
35
+ $ vi resat.log
36
+
37
+ if <tt>/tmp</tt> does not exist
38
+
39
+ = Additional Examples
40
+
41
+ The <i>additional</i> folder contains two additional scenarios which are not ran
42
+ by default:
43
+
44
+ * <tt>follow.yml</tt>: Follow given user
45
+ * <tt>send_message.yml</tt>: Send direct message to given user with given content
46
+
47
+ Both these scenarios require inputs. Inputs are given using the <tt>--define</tt>
48
+ (or <tt>-d</tt>) resat option:
49
+
50
+ $ resat additional/follow -d followed:rgsimon
@@ -0,0 +1,15 @@
1
+ # Follow username specified on command line
2
+ #
3
+ # Usage:
4
+ # resat follow -d followed:rgsimon
5
+
6
+ name: Follow given user
7
+ config: ../config/resat.yaml
8
+ steps:
9
+ - request:
10
+ resource: notifications # Act on the 'notifications' resource
11
+ custom: # Use a custom operation (i.e. not a CRUD operation)
12
+ name: follow # Operation name
13
+ type: post # POST request
14
+ id: $followed # ID of user to follow
15
+ format: xml # Get response in XML format
@@ -0,0 +1,19 @@
1
+ # Send direct message to username specified on command line
2
+ # Note: user must be following you otherwise the request returns 403.
3
+ #
4
+ # Usage:
5
+ # resat send_message -d to:rgsimon -d text:'Hello from resat!'
6
+
7
+ name: Send Direct Message
8
+ config: ../config/resat.yaml
9
+ steps:
10
+ - request:
11
+ resource: direct_messages # Act on the 'direct_messages' resource
12
+ custom: # Use a custom operation (i.e. not a CRUD operation)
13
+ name: new.xml # Operation name
14
+ type: post # POST request
15
+ params:
16
+ - name: user # 'user' parameter
17
+ value: $to # Username
18
+ - name: text # 'text' parameter
19
+ value: $text # Message content
@@ -0,0 +1,40 @@
1
+ # Hostname used for API calls
2
+ host: twitter.com
3
+
4
+ # Port
5
+ # port: 443
6
+
7
+ # Common base URL to all API calls
8
+ base_url:
9
+
10
+ # Use HTTPS?
11
+ use_ssl: yes
12
+
13
+ # Basic auth username if any
14
+ # Uses variable 'user' from the command line
15
+ username: $user
16
+
17
+ # Basic auth password if any
18
+ # Uses variable 'pass' from the command line
19
+ password: $pass
20
+
21
+ # Common request headers for all API calls
22
+ headers:
23
+ # - name: header_name
24
+ # value: header_value
25
+
26
+ # Common parameters for all API calls
27
+ params:
28
+ # - name: param_name
29
+ # value: param_value
30
+
31
+ # Global variables
32
+ variables:
33
+ - name: tweet
34
+ value: 'Checking out resat (http://tinyurl.com/dg8gf9)'
35
+
36
+ # Input file
37
+ # input: input.yml
38
+
39
+ # Output file
40
+ output: output.yml
@@ -0,0 +1,5 @@
1
+ ---
2
+ - name: last_public_tweet
3
+ value: "Novellus&amp;#39; CoolFill CVD Process Advances Tungsten Fill for Sub-32nm ...: Logic devices, though not as aggress.. http://tinyurl.com/csja8w"
4
+ - name: last_friends_tweet
5
+ value: "@rgsimon Hey resat is cool!"
@@ -0,0 +1,31 @@
1
+ # Retrieve public and private recent tweets
2
+ #
3
+ # http://twitter.com/statuses/public_timeline.format
4
+ # http://twitter.com/statuses/public_timeline.format
5
+ name: Twitter Timelines
6
+ config: ../config/resat.yaml
7
+ steps:
8
+ - request:
9
+ resource: statuses
10
+ custom: # Use a custom operation (i.e. not a CRUD operation)
11
+ name: public_timeline.xml # Operation name
12
+ type: get # GET request
13
+ filters:
14
+ - name: extract latest public tweet
15
+ target: body
16
+ extractors:
17
+ - field: statuses/status/text
18
+ variable: last public tweet
19
+ save: yes
20
+ - request:
21
+ resource: statuses
22
+ custom: # Use a custom operation (i.e. not a CRUD operation)
23
+ name: friends_timeline.xml # Operation name
24
+ type: get # GET request
25
+ filters:
26
+ - name: extract latest friends tweet
27
+ target: body
28
+ extractors:
29
+ - field: statuses/status/text
30
+ variable: last friends tweet
31
+ save: yes
@@ -0,0 +1,14 @@
1
+ # Send Tweet
2
+ # http://twitter.com/statuses/update.format
3
+ name: Send Tweet
4
+ config: ../config/resat.yaml
5
+ steps:
6
+ - request:
7
+ resource: statuses # Act on the 'notifications' resource
8
+ custom: # Use a custom operation (i.e. not a CRUD operation)
9
+ name: update.xml # Operation name
10
+ type: post # POST request
11
+ params:
12
+ - name: status
13
+ value: $tweet
14
+
@@ -0,0 +1,145 @@
1
+ # API request
2
+ # See resat.rb for usage information.
3
+ #
4
+
5
+ require 'uri'
6
+ require 'rexml/document'
7
+ require File.join(File.dirname(__FILE__), 'net_patch')
8
+
9
+ module Resat
10
+
11
+ class ApiRequest
12
+ include Kwalify::Util::HashLike
13
+ attr_reader :request, :response, :send_count, :failures
14
+
15
+ # Prepare request so 'send' can be called
16
+ def prepare
17
+ @format ||= 'xml'
18
+ @failures = []
19
+ @send_count = 0
20
+
21
+ # 1. Normalize call fields
22
+ @headers ||= []
23
+ @params ||= []
24
+ # Clone config values so we don't mess with them when expanding variables
25
+ Config.headers.each do |h|
26
+ @headers << { 'name' => h['name'].dup, 'value' => h['value'].dup }
27
+ end if Config.headers
28
+ Config.params.each do |p|
29
+ @params << { 'name' => p['name'].dup, 'value' => p['value'].dup }
30
+ end if Config.params && request_class.REQUEST_HAS_BODY
31
+ Variables.substitute!(@params)
32
+ Variables.substitute!(@headers)
33
+
34
+ # 2. Build URI
35
+ Variables.substitute!(@id) if @id
36
+ uri_class = (@use_ssl || @use_ssl.nil? && Config.use_ssl) ? URI::HTTPS : URI::HTTP
37
+ port = @port || Config.port || uri_class::DEFAULT_PORT
38
+ @uri = uri_class.build( :host => @host || Config.host, :port => port )
39
+ base_url = "/#{@base_url || Config.base_url}/".squeeze('/')
40
+ Variables.substitute!(base_url)
41
+ path = "#{base_url}#{@resource}"
42
+ path = "#{path}/#{@id}" if @id
43
+ path = "#{path}.#{@format}" if @format && !@custom
44
+ path = "#{path}#{@custom.separator}#{@custom.name}" if @custom
45
+ @uri.merge!(path)
46
+
47
+ # 3. Build request
48
+ case @operation
49
+ when 'index', 'show' then request_class = Net::HTTP::Get
50
+ when 'create' then request_class = Net::HTTP::Post
51
+ when 'update' then request_class = Net::HTTP::Put
52
+ when 'destroy' then request_class = Net::HTTP::Delete
53
+ else
54
+ if @custom
55
+ case @custom.type
56
+ when 'get' then request_class = Net::HTTP::Get
57
+ when 'post' then request_class = Net::HTTP::Post
58
+ when 'put' then request_class = Net::HTTP::Put
59
+ when 'delete' then request_class = Net::HTTP::Delete
60
+ end
61
+ else
62
+ @failures << "Missing request operation for request on '#{@resource}'."
63
+ return
64
+ end
65
+ end
66
+ @request = request_class.new(@uri.to_s)
67
+ username = @username || Config.username
68
+ Variables.substitute!(username) if username
69
+ password = @password || Config.password
70
+ Variables.substitute!(password) if password
71
+ if username && password
72
+ @request.basic_auth(username, password)
73
+ end
74
+ form_data = Hash.new
75
+ @headers.each { |header| @request[header['name']] = header['value'] }
76
+ @params.each { |param| form_data[param['name']] = param['value'] }
77
+ @request.set_form_data(form_data) unless form_data.empty?
78
+ Log.request(@request)
79
+
80
+ # 4. Send request and check response code
81
+ @oks = @valid_codes.map { |r| r.to_s } if @valid_codes
82
+ @oks ||= %w{200 201 202 203 204 205 206}
83
+ end
84
+
85
+ # Actually send the request
86
+ def send
87
+ sleep(delay_seconds) # Delay request if needed
88
+ http = Net::HTTP.new(@uri.host, @uri.port)
89
+ http.use_ssl = Config.use_ssl
90
+ begin
91
+ res = http.start { |http| @response = http.request(@request) }
92
+ rescue Exception => e
93
+ @failures << "Exception raised while making request: #{e.message}"
94
+ end
95
+ if @failures.size == 0
96
+ @send_count += 1
97
+ if @oks.include?(res.code)
98
+ Log.response(@response)
99
+ else
100
+ Log.response(@response, false)
101
+ @failures << "Request returned #{res.code}"
102
+ end
103
+ end
104
+ end
105
+
106
+ # Does response include given header or body field?
107
+ def has_response_field?(field, target)
108
+ return unless @response
109
+ return @response.key?(field) if target == 'header'
110
+ doc = REXML::Document.new(@response.body) rescue nil
111
+ return doc && !doc.elements[field].nil?
112
+ end
113
+
114
+ # Get value of response header or body field
115
+ def get_response_field(field, target)
116
+ return unless @response
117
+ return @response[field] if target == 'header'
118
+ doc = REXML::Document.new(@response.body)
119
+ elem = doc.elements[field]
120
+ return elem.get_text.to_s if elem
121
+ end
122
+
123
+ protected
124
+
125
+ # Calculate number of seconds to wait before sending request
126
+ def delay_seconds
127
+ seconds = nil
128
+ if delay = @delay || Config.delay
129
+ min_delay = max_delay = nil
130
+ if delay =~ /([\d]+)\.\.([\d]+)/
131
+ min_delay = Regexp.last_match[1].to_i
132
+ max_delay = Regexp.last_match[2].to_i
133
+ elsif delay.to_i.to_s == delay
134
+ min_delay = max_delay = delay.to_i
135
+ end
136
+ if min_delay && max_delay
137
+ seconds = rand(max_delay - min_delay + 1) + min_delay
138
+ end
139
+ end
140
+ seconds || 0
141
+ end
142
+
143
+ end
144
+
145
+ end