af-addon-tester 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ manifest.json
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in af-addon-tester.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ af-addon-tester
2
+ ===============
3
+
4
+ <img src="http://appfog.com/images/logo.png" />
5
+
6
+ Allows developers to test App Fog add-ons
7
+
8
+ ## Setup ##
9
+
10
+ 1) Clone
11
+
12
+ $ git clone git@github.com:tsantef/af-addon-tester.git
13
+
14
+ 2) Create a manifest.json that points to a test addon
15
+
16
+ Example
17
+
18
+ {
19
+ "id":"myaddon",
20
+ "api":{
21
+ "plans":[
22
+ {"id":"free"}
23
+ ],
24
+ "config_vars": {
25
+ "MYADDON_URL":"http://some.url.com",
26
+ "MYADDON_VAR1":"cats",
27
+ "MYADDON_VAR2":"dogs"
28
+ },
29
+ "test":"http://localhost:4567/myaddon/resources",
30
+ "password":"cavef6azebRewruvecuch",
31
+ "sso_salt":"8ouy3ayLEyOA7HLAKO2Yo"
32
+ }
33
+ }
34
+
35
+
36
+ ## Usage ##
37
+
38
+ $ af-addon-tester <path to manifest>
39
+
40
+
41
+ ## Meta ##
42
+
43
+ Maintained by Tim Santeford.
44
+
45
+ Released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "af-addon-tester/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "af-addon-tester"
7
+ s.version = AppFog::AddonTester::VERSION
8
+ s.default_executable = %q{af-addon-tester}
9
+ s.authors = ["Tim Santeford"]
10
+ s.email = ["tim@phpfog.com"]
11
+ s.homepage = "http://www.appfog.com"
12
+ s.summary = %q{Tests App Fog Add-ons}
13
+ s.description = %q{Allows developers to test App Fog add-ons}
14
+
15
+ s.rubyforge_project = "af-addon-tester"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_runtime_dependency 'json'
23
+ end
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'rubygems'
7
+ require 'json'
8
+ require 'digest/sha1'
9
+ require 'af-addon-tester'
10
+
11
+ manifest_path = ARGV.first
12
+
13
+ begin
14
+ manifest_file = File.open(File.expand_path(manifest_path), 'r')
15
+ manifest_json = manifest_file.readlines.to_s
16
+ manifest = JSON.parse(manifest_json)
17
+
18
+ raise "Missing id" if manifest['id'].nil?
19
+ raise "Missing api section" if manifest['api'].nil?
20
+ raise "Missing api password" if manifest['api']['password'].nil?
21
+ raise "Missing apt test url" if manifest['api']['test'].nil?
22
+ raise "Manifest must have atleast one plan" if manifest['api']['plans'].nil? || manifest['api']['plans'][0].nil? || manifest['api']['plans'][0]['id'].nil?
23
+ raise "missing api config_vars" if manifest['api']['config_vars'].nil?
24
+ raise "Missing api sso_salt" if manifest['api']['sso_salt'].nil?
25
+
26
+ rescue Exception => e
27
+ puts bwhite "#{e.message}. Please specify a valid manifest file."
28
+ exit
29
+ end
30
+
31
+ callback_url = 'http://localhost:9990'
32
+ config_prefix = manifest['id'].gsub('-','_').upcase + '_'
33
+ bad_user = 'bad_user'
34
+ bad_password = 'bad_pass'
35
+
36
+ addon = Rest.new(manifest['api']['test'], manifest['id'], manifest['api']['password'])
37
+ resp = nil
38
+
39
+ validate "Provisioning" do
40
+ params = {}
41
+ payload = { 'customer_id' => manifest['id'], 'plan' => manifest['api']['plans'][0]['id'], 'callback_url' => callback_url, 'options' => '{}' }
42
+ resp = addon.post(manifest['api']['test'], params, JSON.generate(payload))
43
+ failed("response code: #{resp.code} - #{resp.message}") if resp.code != "200"
44
+ passed
45
+ end
46
+
47
+ if resp.code == "200"
48
+ provision_info = nil
49
+ validate "Valid JSON response" do
50
+ begin
51
+ provision_info = JSON.parse(resp.body)
52
+ rescue Exception => e
53
+ failed e.message
54
+ end
55
+ passed
56
+ end
57
+
58
+ unless provision_info.nil?
59
+ validate "Response params" do
60
+ begin
61
+ failed("Missing 'id'") if provision_info['id'].nil? || provision_info['id'] == ""
62
+ rescue Exception => e
63
+ failed e.message
64
+ end
65
+ passed
66
+ end
67
+
68
+ unless provision_info['config'].nil?
69
+ validate "All config keys are in manifest" do
70
+ provision_info['config'].each do |key, value|
71
+ unless manifest['api']['config_vars'].include? key; failed "#{key} not found in config"; end
72
+ end
73
+ end
74
+
75
+ validate "All manifest config keys are in response" do
76
+ manifest['api']['config_vars'].each do |key, value|
77
+ unless provision_info['config'].include? key; failed "#{key} not found in manifest"; end
78
+ end
79
+ end
80
+
81
+ validate "All config keys are prefixed with addon name" do
82
+ provision_info['config'].each do |key, value|
83
+ unless key.start_with? config_prefix; failed "#{key} does not begin with #{config_prefix}"; end
84
+ end
85
+ passed
86
+ end
87
+ end
88
+
89
+ resource_id = provision_info['id']
90
+ unless resource_id.nil?
91
+ validate "Update resource" do
92
+ params = {}
93
+ payload = { 'plan' => 'paid', 'callback_url' => callback_url, 'options' => '{}' }
94
+ resp = addon.put(manifest['api']['test'] + "/#{resource_id}", params, JSON.generate(payload))
95
+ failed("response code: #{resp.code}") if resp.code != "200"
96
+ passed
97
+ end
98
+
99
+ READLEN = 1024 * 10
100
+ reader, writer = IO.pipe
101
+ out = nil
102
+ validate "Callback" do
103
+ child = fork do
104
+ reader.close
105
+ server = TCPServer.open(9990)
106
+ client = server.accept
107
+ writer.write(client.readpartial(READLEN))
108
+ client.write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
109
+ client.close
110
+ writer.close
111
+ end
112
+ sleep(1)
113
+ out = reader.readpartial(READLEN)
114
+ passed
115
+ end
116
+
117
+ callback_info = nil
118
+ isValidCallback = false
119
+ validate "Valid callback response" do
120
+ _, json = out.split("\r\n\r\n")
121
+ begin
122
+ callback_info = JSON.parse(json)
123
+ rescue Exception => e
124
+ failed e.message
125
+ end
126
+ isValidCallback = true
127
+ passed
128
+ end
129
+
130
+ if isValidCallback
131
+ validate "All callback config keys are in manifest" do
132
+ callback_info['config'].each do |key, value|
133
+ unless manifest['api']['config_vars'].include? key; failed "#{key} not found in config"; end
134
+ end
135
+ end
136
+
137
+ validate "All callback manifest config keys are in response" do
138
+ manifest['api']['config_vars'].each do |key, value|
139
+ unless callback_info['config'].include? key; failed "#{key} not found in manifest"; end
140
+ end
141
+ end
142
+
143
+ validate "All callback config keys are prefixed with addon name" do
144
+ callback_info['config'].each do |key, value|
145
+ unless key.start_with? config_prefix; failed "#{key} does not begin with #{config_prefix}"; end
146
+ end
147
+ passed
148
+ end
149
+ end
150
+
151
+ validate "SSO link" do
152
+ timestamp = Time.now.to_i
153
+ authstring = resource_id.to_s + ':' + manifest['api']['sso_salt'] + ':' + timestamp.to_s
154
+ token = Digest::SHA1.hexdigest(authstring)
155
+ resp = addon.get(manifest['api']['test'] + "/#{resource_id}?token=#{token}&timestamp=#{timestamp}")
156
+ passed unless resp.code != "200"
157
+ end
158
+
159
+ validate "Deprovision" do
160
+ params = {}
161
+ payload = { 'customer_id' => manifest['id'], 'plan' => manifest['api']['plans'][0]['id'], 'callback_url' => callback_url, 'options' => '{}' }
162
+ resp = addon.delete(manifest['api']['test'] + "/#{resource_id}", params, JSON.generate(payload))
163
+ failed("response code: #{resp.code}") if resp.code != "200"
164
+ passed
165
+ end
166
+
167
+ end
168
+ end
169
+
170
+ else
171
+ false
172
+ end
173
+
174
+ validate "Bad credentials test" do
175
+ params = {}
176
+ payload = { 'customer_id' => manifest['id'], 'plan' => manifest['api']['plans'][0]['id'], 'callback_url' => callback_url, 'options' => '{}' }
177
+
178
+ addon_bad_auth = Rest.new(manifest['api']['test'], bad_user, bad_password)
179
+ resp = addon.post(manifest['api']['test'], params, JSON.generate(payload))
180
+
181
+ failed if resp.code == "200"
182
+ passed
183
+ end
@@ -0,0 +1,16 @@
1
+ {
2
+ "id":"myaddon",
3
+ "api":{
4
+ "plans":[
5
+ {"id":"free"}
6
+ ],
7
+ "config_vars": {
8
+ "MYADDON_URL":"http://some.url.com",
9
+ "MYADDON_VAR1":"cats",
10
+ "MYADDON_VAR2":"dogs"
11
+ },
12
+ "test":"http://localhost:4567/myaddon/resources",
13
+ "password":"cavef6azebRewruvecuch",
14
+ "sso_salt":"8ouy3ayLEyOA7HLAKO2Yo"
15
+ }
16
+ }
@@ -0,0 +1,58 @@
1
+ def colorize(str, beginColor, endColor = 0)
2
+ "\e[#{beginColor}m#{str}\e[#{endColor}m"
3
+ end
4
+
5
+ #30 Black
6
+ def black(str, endColor = 0)
7
+ colorize(str, "30", endColor)
8
+ end
9
+
10
+ #31 Red
11
+ def red(str, endColor = 0)
12
+ colorize(str, "31", endColor)
13
+ end
14
+
15
+ #32 Green
16
+ def green(str, endColor = 0)
17
+ colorize(str, "32", endColor)
18
+ end
19
+
20
+ #32 Bright Green
21
+ def bgreen(str, endColor = 0)
22
+ colorize(str, "1;32", endColor)
23
+ end
24
+
25
+ #33 Yellow
26
+ def yellow(str, endColor = 0)
27
+ colorize(str, "33", endColor)
28
+ end
29
+
30
+ #34 Blue
31
+ def blue(str, endColor = 0)
32
+ colorize(str, "34", endColor)
33
+ end
34
+
35
+ #35 Magenta
36
+ def magenta(str, endColor = 0)
37
+ colorize(str, "35", endColor)
38
+ end
39
+
40
+ #36 Cyan
41
+ def cyan(str, endColor = 0)
42
+ colorize(str, "36", endColor)
43
+ end
44
+
45
+ #36 Bright Cyan
46
+ def bcyan(str, endColor = 0)
47
+ colorize(str, "1;36", endColor)
48
+ end
49
+
50
+ #37 White
51
+ def white(str, endColor = 0)
52
+ colorize(str, "37", endColor)
53
+ end
54
+
55
+ #37 Bright White
56
+ def bwhite(str, endColor = 0)
57
+ colorize(str, "1;37", endColor)
58
+ end
@@ -0,0 +1,106 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'cgi'
5
+
6
+ class Rest
7
+
8
+ $user = nil
9
+ $password = nil
10
+ $http = nil
11
+ $cookies = {}
12
+ $last_resp = nil
13
+ $last_params = nil
14
+
15
+ $useragent = ''
16
+
17
+ def initialize(url, user = nil, password = nil)
18
+ $user = user
19
+ $password = password
20
+
21
+ uri = URI(url)
22
+ $http = Net::HTTP.new(uri.host, uri.port)
23
+
24
+ if uri.scheme == 'https'
25
+ $http.use_ssl = true
26
+ $http.verify_mode = OpenSSL::SSL::VERIFY_NONE
27
+ else
28
+ $http.use_ssl = false
29
+ end
30
+ end
31
+
32
+ def get(url, params = nil, additional_header = nil)
33
+ header = { 'Cookie' => cookie_to_s, 'User-Agent' => $useragent }
34
+ header = header.merge(additional_header) unless additional_header.nil?
35
+ req = Net::HTTP::Get.new(url, header)
36
+ make_request(req, params)
37
+ end
38
+
39
+ def post(url, params, payload = nil, additional_header = nil)
40
+ header = { 'Cookie' => cookie_to_s, 'User-Agent' => $useragent }
41
+ header = header.merge(additional_header) unless additional_header.nil?
42
+ req = Net::HTTP::Post.new(url, { 'Cookie' => cookie_to_s, 'User-Agent' => $useragent })
43
+ make_request(req, params, payload)
44
+ end
45
+
46
+ def put(url, params, payload = nil)
47
+ req = Net::HTTP::Put.new(url, { 'Cookie' => cookie_to_s, 'User-Agent' => $useragent })
48
+ make_request(req, params, payload)
49
+ end
50
+
51
+ def delete(url, params = nil, payload = nil)
52
+ req = Net::HTTP::Delete.new(url, { 'Cookie' => cookie_to_s, 'User-Agent' => $useragent })
53
+ make_request(req, params, payload)
54
+ end
55
+
56
+ def cookies
57
+ $cookies
58
+ end
59
+ def cookies=(dough)
60
+ $cookies = dough
61
+ end
62
+
63
+ def inspect
64
+ puts "#{bwhite(resp.code)} - #{$last_resp.message}"
65
+ puts $last_params.inspect
66
+ puts "Cookies: " + $cookies.inspect
67
+ puts $last_resp.body
68
+ end
69
+
70
+ private
71
+
72
+ def cookie_to_s
73
+ cookiestr = ''
74
+ $cookies.each do |key, value|
75
+ cookiestr += "#{key}=#{value}, "
76
+ end
77
+ cookiestr[0..-2]
78
+ end
79
+
80
+ def make_request(req, params = nil, payload = nil)
81
+ $last_params = params
82
+ req.basic_auth($user, $password) unless $user.nil?
83
+ req.set_form_data(params, ';') unless params.nil?
84
+
85
+ unless payload.nil?
86
+ req.body = payload
87
+ req.set_content_type('multipart/form-data')
88
+ end
89
+
90
+ begin
91
+ $last_resp = $http.request(req)
92
+
93
+ unless $last_resp['set-cookie'].nil?
94
+ $last_resp['set-cookie'].split(', ').each do |cookie|
95
+ key, value = cookie.split('=')
96
+ $cookies[key] = value
97
+ end
98
+ end
99
+ rescue NoMethodError
100
+ return Net::HTTPInternalServerError.new '1.1', '500', 'Internal server error'
101
+ end
102
+
103
+ $last_resp
104
+ end
105
+
106
+ end
@@ -0,0 +1,22 @@
1
+ class ValidationFailed < StandardError ; end
2
+
3
+ def validate(msg)
4
+ print "#{msg}: "
5
+ begin
6
+ if yield
7
+ puts "[ " + bgreen("Pass") + " ]"
8
+ else
9
+ raise ValidationFailed, ''
10
+ end
11
+ rescue ValidationFailed => fail
12
+ puts "[ " + red("Failed") + " ] " + bwhite(fail.message)
13
+ end
14
+ end
15
+
16
+ def passed(msg = nil)
17
+ true
18
+ end
19
+
20
+ def failed(msg = '')
21
+ raise ValidationFailed, msg
22
+ end
@@ -0,0 +1,5 @@
1
+ module AppFog
2
+ module AddonTester
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ require "af-addon-tester/version"
2
+ require "af-addon-tester/colorize"
3
+ require "af-addon-tester/test"
4
+ require "af-addon-tester/rest"
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: af-addon-tester
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Tim Santeford
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-01-12 00:00:00 -08:00
18
+ default_executable: af-addon-tester
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ description: Allows developers to test App Fog add-ons
33
+ email:
34
+ - tim@phpfog.com
35
+ executables:
36
+ - af-addon-tester
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - .gitignore
43
+ - Gemfile
44
+ - README.md
45
+ - Rakefile
46
+ - af-addon-tester.gemspec
47
+ - bin/af-addon-tester
48
+ - config/manifest.example.json
49
+ - lib/af-addon-tester.rb
50
+ - lib/af-addon-tester/colorize.rb
51
+ - lib/af-addon-tester/rest.rb
52
+ - lib/af-addon-tester/test.rb
53
+ - lib/af-addon-tester/version.rb
54
+ has_rdoc: true
55
+ homepage: http://www.appfog.com
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project: af-addon-tester
80
+ rubygems_version: 1.3.6
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Tests App Fog Add-ons
84
+ test_files: []
85
+