fog 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/.gitignore +1 -1
  2. data/Rakefile +43 -0
  3. data/changelog.txt +39 -0
  4. data/docs/_config.yml +39 -0
  5. data/docs/_layouts/default.html +96 -0
  6. data/docs/_posts/2011-01-01-cdn.markdown +82 -0
  7. data/docs/_posts/2011-01-01-contributing.markdown +228 -0
  8. data/docs/_posts/2011-01-01-dns.markdown +79 -0
  9. data/docs/_posts/2011-01-01-press.markdown +32 -0
  10. data/docs/_posts/2011-01-01-start.markdown +21 -0
  11. data/docs/_posts/2011-01-01-storage.markdown +145 -0
  12. data/docs/_posts/2011-01-01-structure.markdown +78 -0
  13. data/docs/_posts/2011-01-01-users.markdown +33 -0
  14. data/docs/index.markdown +94 -0
  15. data/docs/public/crossdomain.xml +25 -0
  16. data/docs/public/css/fog.css +129 -0
  17. data/docs/public/css/handheld.css +8 -0
  18. data/docs/public/css/style.css +129 -0
  19. data/docs/public/images/.gitignore +3 -0
  20. data/docs/public/js/libs/dd_belatedpng.js +13 -0
  21. data/docs/public/js/libs/jquery-1.4.2.js +6240 -0
  22. data/docs/public/js/libs/jquery-1.4.2.min.js +154 -0
  23. data/docs/public/js/libs/modernizr-1.6.min.js +30 -0
  24. data/docs/public/js/mylibs/.gitignore +3 -0
  25. data/docs/public/js/plugins.js +34 -0
  26. data/docs/public/js/profiling/config.js +59 -0
  27. data/docs/public/js/profiling/yahoo-profiling.css +7 -0
  28. data/docs/public/js/profiling/yahoo-profiling.min.js +39 -0
  29. data/docs/public/js/script.js +26 -0
  30. data/docs/public/robots.txt +5 -0
  31. data/fog.gemspec +6 -5
  32. data/lib/fog.rb +1 -1
  33. data/lib/fog/aws/cloud_formation.rb +1 -1
  34. data/lib/fog/compute/aws.rb +24 -0
  35. data/lib/fog/compute/models/aws/key_pair.rb +19 -1
  36. data/lib/fog/compute/models/aws/server.rb +6 -12
  37. data/lib/fog/compute/models/rackspace/server.rb +7 -7
  38. data/lib/fog/compute/requests/aws/attach_volume.rb +4 -0
  39. data/lib/fog/compute/requests/aws/create_security_group.rb +3 -3
  40. data/lib/fog/compute/requests/aws/create_tags.rb +2 -0
  41. data/lib/fog/compute/requests/aws/create_volume.rb +4 -0
  42. data/lib/fog/compute/requests/aws/delete_tags.rb +43 -0
  43. data/lib/fog/compute/requests/aws/describe_instances.rb +3 -7
  44. data/lib/fog/compute/requests/aws/describe_snapshots.rb +4 -6
  45. data/lib/fog/compute/requests/aws/describe_volumes.rb +2 -6
  46. data/lib/fog/core.rb +1 -0
  47. data/lib/fog/core/connection.rb +1 -1
  48. data/lib/fog/core/credentials.rb +5 -1
  49. data/lib/fog/core/parser.rb +1 -2
  50. data/lib/fog/core/ssh.rb +2 -3
  51. data/lib/fog/dns/models/aws/record.rb +1 -1
  52. data/lib/fog/dns/models/bluebox/zone.rb +1 -0
  53. data/lib/fog/storage/models/rackspace/directory.rb +8 -2
  54. data/lib/fog/storage/rackspace.rb +3 -1
  55. data/lib/fog/storage/requests/rackspace/delete_container.rb +1 -1
  56. data/lib/fog/storage/requests/rackspace/delete_object.rb +1 -1
  57. data/lib/fog/storage/requests/rackspace/get_container.rb +1 -1
  58. data/lib/fog/storage/requests/rackspace/get_object.rb +1 -1
  59. data/lib/fog/storage/requests/rackspace/head_container.rb +1 -1
  60. data/lib/fog/storage/requests/rackspace/head_object.rb +2 -2
  61. data/lib/fog/storage/requests/rackspace/put_container.rb +1 -1
  62. data/lib/fog/storage/requests/rackspace/put_object.rb +1 -1
  63. data/spec/spec_helper.rb +0 -4
  64. data/tests/aws/requests/cloud_formation/stack_tests.rb +16 -5
  65. data/tests/aws/requests/rds/instance_tests.rb +4 -2
  66. data/tests/compute/models/aws/address_tests.rb +16 -0
  67. data/tests/compute/models/aws/addresses_tests.rb +5 -0
  68. data/tests/compute/models/aws/key_pair_tests.rb +27 -0
  69. data/tests/compute/models/aws/key_pairs_tests.rb +5 -0
  70. data/tests/compute/models/aws/security_group_tests.rb +5 -0
  71. data/tests/compute/models/aws/security_groups.rb +5 -0
  72. data/tests/compute/models/aws/server_tests.rb +39 -0
  73. data/tests/compute/models/aws/snapshot_tests.rb +10 -0
  74. data/tests/compute/models/aws/snapshots_tests.rb +10 -0
  75. data/tests/compute/models/aws/volume_tests.rb +26 -0
  76. data/tests/compute/models/aws/volumes_tests.rb +5 -0
  77. data/tests/core/credential_tests.rb +36 -0
  78. data/tests/storage/requests/aws/object_tests.rb +1 -1
  79. data/tests/storage/requests/google/object_tests.rb +1 -1
  80. data/tests/storage/requests/rackspace/object_tests.rb +8 -0
  81. metadata +118 -75
  82. data/spec/aws/models/compute/address_spec.rb +0 -103
  83. data/spec/aws/models/compute/addresses_spec.rb +0 -70
  84. data/spec/aws/models/compute/key_pair_spec.rb +0 -86
  85. data/spec/aws/models/compute/key_pairs_spec.rb +0 -71
  86. data/spec/aws/models/compute/security_group_spec.rb +0 -88
  87. data/spec/aws/models/compute/security_groups_spec.rb +0 -71
  88. data/spec/aws/models/compute/server_spec.rb +0 -105
  89. data/spec/aws/models/compute/snapshot_spec.rb +0 -79
  90. data/spec/aws/models/compute/snapshots_spec.rb +0 -85
  91. data/spec/aws/models/compute/volume_spec.rb +0 -174
  92. data/spec/aws/models/compute/volumes_spec.rb +0 -71
  93. data/tests/compute/models/aws/server_monitor_tests.rb +0 -47
data/.gitignore CHANGED
@@ -4,7 +4,7 @@
4
4
  *.sw?
5
5
  .bundle
6
6
  .DS_Store
7
- _site/*
7
+ docs/_site/*
8
8
  coverage
9
9
  doc/*
10
10
  Gemfile.lock
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
3
  require 'date'
4
+ require 'fog'
4
5
 
5
6
  #############################################################################
6
7
  #
@@ -130,3 +131,45 @@ task :validate do
130
131
  exit!
131
132
  end
132
133
  end
134
+
135
+ task :docs do
136
+ # build the docs locally
137
+ sh "jekyll docs docs/_site"
138
+
139
+ # connect to storage provider and write files to versioned 'folder'
140
+ Fog.credential = :geemus
141
+ storage = Fog::Storage.new(:provider => 'AWS')
142
+ directory = storage.directories.new(:key => 'fog.io')
143
+ for file_path in Dir.glob('docs/_site/**/*')
144
+ next if File.directory?(file_path)
145
+ file_name = file_path.gsub('docs/_site/', '')
146
+ key = '' << version << '/' << file_name
147
+ Formatador.redisplay(' ' * 80) # clear last line
148
+ Formatador.redisplay('Uploading ' << key)
149
+ if File.extname(file_name) == '.html'
150
+ # rewrite links with version
151
+ body = File.read(file_path)
152
+ body.gsub!(/href="\//, 'href="/' << version << '/')
153
+ content_type = 'text/html'
154
+ else
155
+ body = File.open(file_path)
156
+ content_type = nil # leave it up to mime-types
157
+ end
158
+ directory.files.create(
159
+ :body => body,
160
+ :content_type => content_type,
161
+ :key => key,
162
+ :public => true
163
+ )
164
+ end
165
+
166
+ # write base index with redirect to new version
167
+ directory.files.create(
168
+ :body => '<!doctype html><head><script>window.location = "http://fog.io/' << version << '/index.html"</script></head></html>',
169
+ :content_type => 'text/html',
170
+ :key => 'index.html',
171
+ :public => true
172
+ )
173
+
174
+ Formatador.display_line
175
+ end
@@ -1,3 +1,41 @@
1
+ 0.7.2 04/05/2011
2
+ ================
3
+
4
+ [aws|cloudformation]
5
+ enabling region parameter for AWS cloud formation requests. thanks flessa
6
+
7
+ [compute]
8
+ [aws]
9
+ remove CGI escape from create_security_group. thanks coliver
10
+ replace remaining specs with shindo tests
11
+ update #keypairs reference to #key_pairs. thanks pvande
12
+ Emulate volume unavailability when mocking. thanks mtodd
13
+ Raise error on non-existent snapshot for create_volume. thanks mtodd
14
+ Add tag to resource data when creating a mock tag. thanks betamatt
15
+ Add tag filtering to mocks for snapshots, volumes, instances. thanks betamatt
16
+ Add delete_tags mock. thanks betamatt
17
+ Support tag-key, tag-value, tag:key filters. thanks betamatt
18
+ Correct name of dns-filter filter. thanks betamatt
19
+ allow key pair to write into properly chmodded files. thanks gerred
20
+ Making key pair names a little less potentially brittle. thanks gerred
21
+ [rackspace]
22
+ fix public_ip_address accessors
23
+
24
+ [core]
25
+ updated base parser to not eagerly strip return data. thanks bdorry
26
+ don't escape ssh commands. thanks pvande
27
+ Deal with missing FOG_RC/HOME env vars better. thanks outerim
28
+ fix push parser block passed to excon > 0.6.0
29
+
30
+ [docs]
31
+ move fog.io stuff into docs directory
32
+ pull in and format data from README, wiki, blog posts
33
+
34
+ [storage|rackspace]
35
+ head_object should use HEAD. thanks nate
36
+ Support for serving Rackspace CDN objects via SSL. thanks minter
37
+ Use URI.escape instead of CGI.escape. thanks minter
38
+
1
39
  0.7.1 03/21/2011
2
40
  ================
3
41
 
@@ -146,3 +184,4 @@ Bug fixes.
146
184
  [rackspace|compute] get new auth token when one expires
147
185
  [rackspace|storage] remove nil query params in requests
148
186
  [terremark|ecloud] getting started on ecloud specific implementation
187
+
@@ -0,0 +1,39 @@
1
+ safe: false
2
+ auto: false
3
+ server: false
4
+ server_port: 4000
5
+
6
+ source: .
7
+ destination: ./_site
8
+ plugins: ./_plugins
9
+
10
+ future: true
11
+ lsi: false
12
+ pygments: false
13
+ markdown: maruku
14
+ permalink: none
15
+
16
+ maruku:
17
+ use_tex: false
18
+ use_divs: false
19
+ png_engine: blahtex
20
+ png_dir: images/latex
21
+ png_url: /images/latex
22
+
23
+ rdiscount:
24
+ extensions: []
25
+
26
+ kramdown:
27
+ auto_ids: true,
28
+ footnote_nr: 1
29
+ entity_output: as_char
30
+ toc_levels: 1..6
31
+ use_coderay: false
32
+
33
+ coderay:
34
+ coderay_wrap: div
35
+ coderay_line_numbers: inline
36
+ coderay_line_numbers_start: 1
37
+ coderay_tab_width: 4
38
+ coderay_bold_every: 10
39
+ coderay_css: style
@@ -0,0 +1,96 @@
1
+ <!doctype html>
2
+
3
+ <!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
4
+ <!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
5
+ <!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
6
+ <!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
7
+ <!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
8
+ <head>
9
+ <meta charset="utf-8">
10
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
11
+
12
+ <title>fog - {{ page.title }}</title>
13
+ <meta name="description" content="">
14
+ <meta name="author" content="">
15
+
16
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
17
+
18
+ <!--
19
+ <link rel="shortcut icon" href="public/favicon.ico">
20
+ <link rel="apple-touch-icon" href="public/apple-touch-icon.png">
21
+ -->
22
+
23
+ <link rel="stylesheet" href="public/css/style.css?v=2">
24
+ <link rel="stylesheet" href="public/css/fog.css?v=2">
25
+ <script src="public/js/libs/modernizr-1.6.min.js"></script>
26
+
27
+ </head>
28
+
29
+ <body>
30
+
31
+ <div id="container">
32
+ <header>
33
+ <a href="/"><img src="public/images/fog.png" title="fog" /></a>
34
+ <h1>{{ page.title }}</h1>
35
+ <dl>
36
+ <dt>version</dt><dd>v0.7.2</dd>
37
+ <dt>install</dt><dd><code>gem install fog</code></dd>
38
+ <dt>source</dt><dd><a href="http://github.com/geemus/fog">geemus/fog</a></dd>
39
+ </dl>
40
+ </header>
41
+
42
+ <div id="main">
43
+
44
+ {{ content }}
45
+
46
+ <h2>See Also:</h2>
47
+ <ul>
48
+ <li><a href="/">Home</a></li>
49
+ {% for post in site.posts %}
50
+ <li><a href="{{ post.url }}">{{ post.title }}</a></li>
51
+ {% endfor %}
52
+ </ul>
53
+ </div>
54
+
55
+ <footer>
56
+ sponsored by
57
+ <img height="20px" src="public/images/engineyard.png" title="engineyard" />
58
+ </footer>
59
+ </div> <!-- end of #container -->
60
+
61
+
62
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
63
+ <script>!window.jQuery && document.write(unescape('%3Cscript src="public/js/libs/jquery-1.4.2.js"%3E%3C/script%3E'))</script>
64
+
65
+
66
+ <!-- scripts concatenated and minified via ant build script-->
67
+ <script src="public/js/plugins.js"></script>
68
+ <script src="public/js/script.js"></script>
69
+ <!-- end concatenated and minified scripts-->
70
+
71
+
72
+ <!--[if lt IE 7 ]>
73
+ <script src="public/js/libs/dd_belatedpng.js"></script>
74
+ <script> DD_belatedPNG.fix('img, .png_bg'); </script>
75
+ <![endif]-->
76
+
77
+ <!-- yui profiler and profileviewer - remove for production -->
78
+ <script src="public/js/profiling/yahoo-profiling.min.js"></script>
79
+ <script src="public/js/profiling/config.js"></script>
80
+ <!-- end profiling code -->
81
+
82
+
83
+ <!-- change the UA-XXXXX-X to be your site's ID -->
84
+ <script>
85
+ var _gaq = [['_setAccount', 'UA-301159-7'], ['_trackPageview']];
86
+ (function(d, t) {
87
+ var g = d.createElement(t),
88
+ s = d.getElementsByTagName(t)[0];
89
+ g.async = true;
90
+ g.src = ('https:' == location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
91
+ s.parentNode.insertBefore(g, s);
92
+ })(document, 'script');
93
+ </script>
94
+
95
+ </body>
96
+ </html>
@@ -0,0 +1,82 @@
1
+ ---
2
+ layout: default
3
+ title: cdn
4
+ ---
5
+
6
+ Faster websites are better. <a href="http://www.websiteoptimization.com/speed/tweak/design-factors/">Better experience</a>, <a href="http://exp-platform.com/Documents/IEEEComputer2007OnlineExperiments.pdf">better sales</a>, <a href="http://www.stevesouders.com/blog/2009/07/27/wikia-fast-pages-retain-users/">you name it</a>. Unfortunately, making a website faster can be tough. Thankfully a content distribution network, or CDN, can give you great performance bang for your buck. A CDN helps speed things up by putting copies of your files closer to your users. It's like the difference between pizza delivery from across the street and pizza delivery from the next town over.
7
+
8
+ The ease and deliciousness are the good news, but until recently CDN's were only available in the big leagues via 'my business guys will talk to your business guys' deals. Fortunately for us, Amazon recently updated <a href="http://aws.amazon.com/cloudfront/">CloudFront</a>, their CDN service, to allow us to get these benefits with just a credit card and an API call. So now we'll see how you can spend a few minutes to save your users countless hours of load time.
9
+
10
+ ## Preliminaries
11
+
12
+ First, make sure you have fog installed:
13
+
14
+ gem install fog
15
+
16
+ Now you'll need to <a href="https://aws-portal.amazon.com/gp/aws/developer/subscription/index.html?productCode=AmazonCloudFront">sign up for Cloudfront</a>. Gather up the credentials your new credentials to initialize a connection to the service:
17
+
18
+ require 'fog'
19
+
20
+ # create a connection to the service
21
+ cdn = Fog::CDN.new({
22
+ :provider =&gt; 'AWS',
23
+ :aws_access_key_id =&gt; AWS_ACCESS_KEY_ID,
24
+ :aws_secret_access_key =&gt; AWS_SECRET_ACCESS_KEY
25
+ }
26
+
27
+ ## Setting Up Your CDN
28
+
29
+ Now you'll need to create a 'distribution' which represents a mapping from the CDN to your domain. For the examples we'll pretend we are working on 'http://www.example.com', but you can just switch it to your actual domain. Some <a href="http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/CreateDistribution.html">other options</a> are available, but the only other one we need to fill in is OriginProtocolPolicy. This sets what to do about http vs https. We will use 'match-viewer' which returns the same protocol as the request, but you can also choose 'http-only' which always returns http responses.
30
+
31
+ data = cdn.post_distribution({
32
+ 'CustomOrigin' =&gt; {
33
+ 'DNSName' =&gt; 'www.example.com',
34
+ 'OriginProtocolPolicy' =&gt; 'match-viewer'
35
+ }
36
+ })
37
+
38
+ # parse the response for stuff you'll need later
39
+ distribution_id = data.body['Id']
40
+ caller_reference = data.body['CallerReference']
41
+ etag = data.headers['ETag']
42
+ cdn_domain_name = data.body['DomainName']
43
+
44
+ # wait for the updates to propogate
45
+ Fog.wait_for {
46
+ cdn.get_distribution(distribution_id).body['Status'] ## 'Deployed'
47
+ }
48
+
49
+ ## Getting Served
50
+
51
+ With the domain name from the distribution in hand you should now be ready to serve content from the edge. All you need to do is start replacing urls like `http://www.example.com/stylesheets/foo.css` with `#{cdn_domain_name}/stylesheets/foo.css`. Just because you can do something doesn't always mean you should though. Dynamic pages are not really well suited to CDN storage, since CDN content will be the same for every user. Fortunately some of your most used content is a great fit. By just switching over your images, javascripts and stylesheets you can have an impact for each and every one of your users.
52
+
53
+ Congrats, your site is faster! By default the urls aren't very pretty, something like `http://d1xdx2sah5udd0.cloudfront.net/stylesheets/foo.css`. Thankfully you can use CNAME config options to utilize something like `http://assets.example.com/stylesheets/foo.css`, if you are interested in learning more about this let me know in the comments.
54
+
55
+ ## Cleaning Up
56
+
57
+ But, just in case you need to update things I'll run through how you can make changes. In my case I just want to clean up after myself, so I'll use the distribution_id and ETag from before to disable the distribution. We need to use the ETag as well because it provides a way to refer to different versions of the same distribution and ensures we are updating the version that we think we are.
58
+
59
+ data = cdn.put_distribution_config(
60
+ distribution_id,
61
+ etag,
62
+ {
63
+ 'CustomOrigin' =&gt; {
64
+ 'DNSName' =&gt; 'www.example.com',
65
+ 'OriginProtocolPolicy' =&gt; 'match-viewer'
66
+ },
67
+ 'CallerReference' =&gt; caller_reference,
68
+ 'Enabled' =&gt; 'false'
69
+ }
70
+ )
71
+
72
+ # parse the updated etag
73
+ etag = data.headers['ETag']
74
+
75
+ Now you just need to wait for the update to happen like before and once its disabled we can delete it:
76
+
77
+ Fog.wait_for {
78
+ cdn.get_distribution(distribution_id).body['Status'] ## 'Deployed'
79
+ }
80
+ cdn.delete_distribution(distribution_id, etag)
81
+
82
+ Thats it, now go forth and speed up some load times!
@@ -0,0 +1,228 @@
1
+ ---
2
+ layout: default
3
+ title: Contributing
4
+ ---
5
+
6
+ First off, high five for coming to visit this page. You are my new hero.
7
+
8
+ ## Overview
9
+
10
+ * Organize your patches by keeping all related changes together in a topic branch.
11
+ * Rebase your branch against master before submitting a pull request (and squish any 'oops' or work in progress commits).
12
+ * Submit changes as pull requests describing what the changes should cover and referencing issues (if any).
13
+ * Use 'tags' in your commits to indicate the scope, so things like '\[aws|compute\] fixed something'.
14
+ * Write and run tests. Tests should follow through usage workflows and ought to pass both with mocking on and off.
15
+
16
+ ## Deep dive
17
+
18
+ Now then, some of the what makes it tick and why. For simplicity let's pretend you want to implement a new service, from scratch. I will walk through the requisite pieces and important things to keep in mind as you go.
19
+
20
+ But, before I dive too deep, I'll leave you with an out. Other great ways to contribute are fixing bugs, writing documentation or helping port other projects to use fog. That way everybody wins!
21
+
22
+ ## The Service
23
+
24
+ First and foremost you'll need to create a service, which should start from something like:
25
+
26
+ module Fog
27
+ class TheService < Fog::Service
28
+
29
+ requires :necessary_credential
30
+
31
+ model_path 'path/to/models'
32
+ collection 'name_of_collection'
33
+ model 'name_of_model'
34
+
35
+ request_path 'path/to/requests'
36
+ request 'name_of_request'
37
+
38
+ class Mock
39
+ include Collections
40
+ end
41
+
42
+ class Real
43
+ include Collections
44
+ end
45
+
46
+ end
47
+ end
48
+
49
+ ### Highlights:
50
+ * we segregate between real and mock so it is easier to add stuff to one or the other later.
51
+ * this is where any shared stuff will go, like making/signing requests
52
+
53
+ ## Requests
54
+
55
+ The next thing to bite off are the requests. fog is all about making cloud services easier to use and move between, but requests are not where this happens. Requests should map closely to the actual api requests (you should be able to directly reference the api docs and vice versa). In particular, try to keep the output of any data parsing as close to the actual format as possible. This makes implementation and maintenance much easier and provides a solid foundation for models to build nice things on top of. I generally end up working on stuff to get/list details first and then filling in create/destroy pairs and other requests.
56
+ You start with something like this:
57
+
58
+ <pre>
59
+ module Fog
60
+ class TheService
61
+
62
+ class Real
63
+
64
+ def request(*args)
65
+ end
66
+
67
+ end
68
+
69
+ class Mock
70
+
71
+ def request(*args)
72
+ Fog::Mock.not_implemented
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+ end
79
+ </pre>
80
+
81
+ ### Highlights:
82
+ * You should define the method twice, once for the real implementation and once for mocked (they should take the same arguments).
83
+ * The mock versions should just start out by raising a not implemented error, you can come back and fill this in later.
84
+ * The real version should make a request, probably by a method defined on the real class in the service you defined earlier.
85
+ * Each request should either return an Excon::Response (with a parsed body where appropriate) or raise an error.
86
+
87
+ ## Tests
88
+
89
+ Now would be a good time to write some tests to make sure what you have written works (and will continue to). I've tried a couple variations on testing in the past, but have settled on consolidated lifetime testing. These vary enough that its hard to give a single simple example, but you can see many examples in "tests/compute/requests/aws":https://github.com/geemus/fog/tree/master/tests/compute/requests/aws/.
90
+
91
+ ### Highlights:
92
+ * Reuse the same objects and take them through their whole life cycle (this is much faster, and most of the time if one portion fails the others would anyway).
93
+ * Test the format of the output to ensure parsers match expectations (check the provider's api docs) and that mocks return matching data.
94
+ * Test common failure cases and their behavior, you'll need to know how the service acts in these cases to make better mocks.
95
+
96
+ ## Models
97
+
98
+ You could also skip to the mocks here if you wanted, but I usually find the more time I spend working with the service the easier it is to build mocks. The models are the real pay dirt, you have slogged through low level requests that map to the provider api and now you want a nice interface. This is where models and collections come in. Collections provide access to lists of data on the provider and for creating new objects. Models represent the individual objects.
99
+
100
+ If you know which object you'd like to represent you should start with the collection. When naming, please refer to the names that have been chosen for other services. I haven't standardized all nouns yet, but a few are already shared (Flavor, Image, Server)
101
+ An example servers collection:
102
+
103
+ require 'fog/collection'
104
+ require 'fog/theservice/models/server'
105
+ module Fog
106
+ class TheService
107
+
108
+ class Servers < Fog::Collection
109
+
110
+ model Fog::TheService::Server
111
+
112
+ def all
113
+ # get list of servers
114
+ load(data) # data is an array of attribute hashes
115
+ end
116
+
117
+ def get(identity)
118
+ # get server matching id
119
+ new(data) # data is an attribute hash
120
+ rescue Excon::Errors::NotFound
121
+ nil
122
+ end
123
+
124
+ end
125
+
126
+ end
127
+ end
128
+
129
+ ### Highlights
130
+ * First make an accessor in the Collections model so it will be included in Real and Mock.
131
+ * `#model` will take a reference to the class that will be instantiated to represent individual objects.
132
+ * `#all` should get a list of servers from the provider and pass an array of attribute hashes, one per server, to load.
133
+ * `#get` should take an identity reference and instantiate a new model object with an attribute hash returned from the remote server, or return nil of no such object exists.
134
+
135
+ Models handle remapping attributes into friendlier names and providing the rest of the interface.
136
+ An example model:
137
+
138
+ require 'fog/model'
139
+ module Fog
140
+ module TheService
141
+
142
+ class Server << Fog::Model
143
+
144
+ identity :id
145
+
146
+ attribute :state, 'StatusValue'
147
+
148
+ def destroy
149
+ requires :identity
150
+ connection.destroy_server(identity)
151
+ true
152
+ end
153
+
154
+ def ready?
155
+ state == 'running'
156
+ end
157
+
158
+ def save
159
+ requires ...
160
+ connection.create_server(options)
161
+ true
162
+ end
163
+
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+ ### Highlights
170
+ * `#identity` captures the id/name that the objects are identified by and takes the same arguments as attribute.
171
+ * `#attribute` takes the name to make a variable available as and one or more aliases that parsers/requests will return this value as.
172
+ * `#destroy` will require the identity of the model and should destroy it and return true.
173
+ * `#ready?` should return whether the object has finished being initialized (where appropriate).
174
+ * `#save` should take any required objects and instantiate the object on the provider's service.
175
+ * These models just rely on underlying collections and requests, so it should not be necessary at this level to distinguish between Real and Mock methods.
176
+
177
+ ## Mocks
178
+
179
+ Mocks provide a powerful tool for users of fog to experiment with their implementations much more quickly and without incurring costs. I usually save these for last, as implementing the requests and models provide some necessary context to finally put the mocks together. Your services mock class should have a data method that will return mocked data like so:
180
+
181
+ module Fog
182
+ module TheService
183
+
184
+ class Mock
185
+ def self.data
186
+ @data ||= Hash.new do |hash, key|
187
+ hash[key] = {}
188
+ end
189
+ end
190
+ end
191
+
192
+ end
193
+ end
194
+
195
+ The keys in this hash should represent a unique identifier of the user accessing the data and the value assigned should contain any default data that a new user might have. Any implemented mock requests should then return data retrieved from here or raise an error.
196
+ For instance:
197
+
198
+ module Fog
199
+ module TheService
200
+
201
+ class Mock
202
+
203
+ def destroy_server(server_identity)
204
+ if data = @data[:servers].delete(server_identity)
205
+ response = Excon::Response.new
206
+ response.status = 202
207
+ response.body = data
208
+ response
209
+ else
210
+ raise Fog::TheService::NotFound
211
+ end
212
+ end
213
+
214
+ end
215
+
216
+ end
217
+ end
218
+
219
+ ### Highlights
220
+ * Mock requests should return the same type of data as an already parsed real response or should return the same error as a real problem.
221
+ * By mocking at this low level, higher level functions are automatically mocked out for you.
222
+ * The extra rigorous tests related to output formatting and error messages should help keep you honest, and each should pass in both mocked and unmocked modes.
223
+
224
+ ## Summary
225
+
226
+ That provides a lot more detail than you will probably need right away, but hopefully you can refer back to different sections as you need them. If you have any questions send me a github message or email me (address is on my profile). You should always start development by creating your own fork. When you feel confident about your fork, send me a pull request. Be forewarned that I may edit some things before it gets to master, but I'll do my best to take care of this in a timely manner.
227
+
228
+ Thanks again for your interest and let me know if there is anything else I can do to help.