Permalink
Browse files

Initial pass at hypermedia API.

Change-Id: I623e48fa58e2a3a67ed74bff39d74093f6e57889
  • Loading branch information...
1 parent d9e0f2e commit 47c44d9962535be59cc28fa6728736f392463bc1 @mmb mmb committed Oct 30, 2012
Showing with 2,637 additions and 28 deletions.
  1. +1 −0 .gitignore
  2. +115 −1 README.md
  3. +6 −1 micro/Gemfile
  4. +12 −0 micro/config.ru
  5. +20 −0 micro/lib/micro.rb
  6. +4 −0 micro/lib/micro/api.rb
  7. +5 −0 micro/lib/micro/api/engine.rb
  8. +28 −0 micro/lib/micro/api/engine/expect_helper.rb
  9. +25 −0 micro/lib/micro/api/engine/json_serializable.rb
  10. +30 −0 micro/lib/micro/api/engine/link.rb
  11. +104 −0 micro/lib/micro/api/engine/media_type.rb
  12. +61 −0 micro/lib/micro/api/engine/media_type_serial.rb
  13. +28 −0 micro/lib/micro/api/media_type.rb
  14. +35 −0 micro/lib/micro/api/media_type/administrator.rb
  15. +37 −0 micro/lib/micro/api/media_type/domain_name.rb
  16. +45 −0 micro/lib/micro/api/media_type/micro_cloud.rb
  17. +39 −0 micro/lib/micro/api/media_type/network_health.rb
  18. +45 −0 micro/lib/micro/api/media_type/network_interface.rb
  19. +38 −0 micro/lib/micro/api/media_type/service.rb
  20. +32 −0 micro/lib/micro/api/media_type/service_list.rb
  21. +3 −0 micro/lib/micro/api/route.rb
  22. +66 −0 micro/lib/micro/api/route/administrator.rb
  23. +112 −0 micro/lib/micro/api/route/domain_name.rb
  24. +35 −0 micro/lib/micro/api/route/graph.rb
  25. +71 −0 micro/lib/micro/api/route/micro_cloud.rb
  26. +43 −0 micro/lib/micro/api/route/network_health.rb
  27. +72 −0 micro/lib/micro/api/route/network_interface.rb
  28. +61 −0 micro/lib/micro/api/route/service.rb
  29. +51 −0 micro/lib/micro/api/route/service_list.rb
  30. +31 −0 micro/lib/micro/api/server.rb
  31. +15 −2 micro/lib/micro/apply_spec.rb
  32. +6 −1 micro/lib/micro/bosh_wrapper.rb
  33. +5 −3 micro/lib/micro/config_file.rb
  34. +57 −8 micro/lib/micro/dnsmasq.rb
  35. +17 −0 micro/lib/micro/local_ip.rb
  36. +27 −0 micro/lib/micro/monitored_process.rb
  37. +41 −0 micro/lib/micro/monitored_process_group.rb
  38. +50 −0 micro/lib/micro/monitored_process_groups.rb
  39. +61 −0 micro/lib/micro/network_health.rb
  40. +8 −2 micro/lib/micro/network_interface.rb
  41. +7 −1 micro/lib/micro/network_interfaces_file.rb
  42. +63 −0 micro/lib/micro/service_config.rb
  43. +20 −0 micro/lib/micro/shell_raiser.rb
  44. +10 −0 micro/mcf-api.conf
  45. +9 −0 micro/public/assets/css/bootstrap-responsive.min.css
  46. +9 −0 micro/public/assets/css/bootstrap.min.css
  47. 0 micro/public/assets/css/mcf.css
  48. BIN micro/public/assets/img/glyphicons-halflings-white.png
  49. BIN micro/public/assets/img/glyphicons-halflings.png
  50. +6 −0 micro/public/assets/js/bootstrap.min.js
  51. +2 −0 micro/public/assets/js/jquery-1.8.3.min.js
  52. +205 −0 micro/public/assets/js/mcf.js
  53. +232 −0 micro/public/index.html
  54. +157 −0 micro/spec/api/engine/media_type_spec.rb
  55. +29 −0 micro/spec/apply_spec_spec.rb
  56. +33 −0 micro/spec/config_file_spec.rb
  57. +65 −8 micro/spec/dnsmasq_spec.rb
  58. +13 −0 micro/spec/local_ip_spec.rb
  59. +130 −0 micro/spec/monitored_process_group_spec.rb
  60. +50 −0 micro/spec/monitored_process_groups_spec.rb
  61. +38 −0 micro/spec/monitored_process_spec.rb
  62. +17 −1 micro/spec/network_interfaces_file_spec.rb
View
@@ -0,0 +1 @@
+*~
View
116 README.md
@@ -4,4 +4,118 @@ This repository contains build scripts, the console application, the REST API
and the web GUI for the Micro Cloud Foundry.
To file a bug against Cloud Foundry Open Source and its components, sign up
-and use our bug tracking system: http://cloudfoundry.atlassian.net
+and use our bug tracking system: http://cloudfoundry.atlassian.net/
+
+API
+===
+
+The Micro Cloud has a hypermedia/REST API. Starting at the root resource, each
+resource is exposed via named links in the _links element. The links give the
+client the expected HTTP method, URL and content type for each state.
+
+Clients should start at the root and navigate through links for each request.
+They should use the methods, URLs and content types that are returned and
+not build any URLs themselves.
+
+The API is browsable using a browser with a JSON formatting plugin like
+JSONView installed.
+
+API Capabilities
+================
+
+Set HTTP proxy
+--------------
+
+/ edit link / send request with http_proxy set
+
+Enable disable internet connection (offline mode)
+-------------------------------------------------
+
+/ edit link / send request with internet_connected = true or false
+
+Power off VM
+------------
+
+/ edit link / send request with is_powered_on = false
+
+Set administrator email
+-----------------------
+
+/ administrator link / edit link / send request with email field set
+
+Set administrator password
+--------------------------
+
+/ administrator link / edit link / send request with password field set
+
+Set domain to a domain name
+---------------------------
+
+/ domain_name link / edit link / send request with name field set
+
+Set domain using a token from cloudfoundry.com
+----------------------------------------------
+
+/ domain_name link / edit link / send request with token field set
+
+Refresh DNS
+----------
+
+Update the Micro Cloud DynDNS hostname with the VM's current IP address.
+
+/ domain_name link / edit link / send request with synched = true
+
+Configure the network interface statically
+------------------------------------------
+
+/ network_interface link / edit link / send request with ip_address, netmask,
+gateway and nameservers set
+
+Configure the network interface to use DHCP
+------------------------------------------
+
+/ network_interface link / edit link / send request with is_dhcp = true
+
+Start or stop a service
+-----------------------
+
+/ services link / select service by name and follow edit link / send request
+with enabled = true or false
+
+API Architecture
+================
+
+Media Types
+-----------
+
+Each resource has a media type class that is a subclass of Engine::MediaType.
+Subclasses define a MediaType class constant for their content type and a
+set of links to other resources.
+
+Links
+-----
+
+The format of the links in JSON is based on the link format of HAL with a few
+additions: http://stateless.co/hal_specification.html
+
+Serialization and Content Types
+-------------------------------
+
+The MediaTypeSerial Rack middleware automatically parses incoming JSON
+into the correct MediaType subclass based on its content type. The
+media type object is set as env['media_type_object'] in Rack environment.
+
+This middleware also serializes MediaType subclasses to JSON based on their
+instance variables and sets the correct content type on responses.
+
+Routes
+------
+
+Classes in the Routes module are automatically registered in the Sinatra
+app.
+
+Code Organization
+-----------------
+
+The Engine module is intended for generic hypermedia API code that might
+eventually be moved to a separate project.
View
@@ -22,7 +22,8 @@ gem 'rest-client'
gem "statemachine"
gem "progressbar"
-gem 'vmc'
+gem 'ruby-graphviz'
+gem 'sinatra'
group :test do
gem "rcov_analyzer", :platforms => :ruby_18
@@ -32,3 +33,7 @@ group :test do
gem "simplecov", :platforms => :ruby_19
gem "simplecov-rcov", :platforms => :ruby_19
end
+
+group :development do
+ gem 'shotgun'
+end
View
@@ -0,0 +1,12 @@
+$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
+
+# TODO: remove this
+$:.unshift '/var/vcap/bosh/agent/lib'
+
+require 'micro'
+
+use VCAP::Micro::Api::Engine::Rack::MediaTypeSerial
+
+map '/api' do
+ run VCAP::Micro::Api::Server
+end
View
@@ -0,0 +1,20 @@
+require 'micro/bosh_wrapper'
+require 'micro/service_config'
+
+require 'micro/api'
+require 'micro/apply_spec'
+require 'micro/config_file'
+require 'micro/commenter'
+require 'micro/console'
+require 'micro/dns_api_client'
+require 'micro/dnsmasq'
+require 'micro/identity'
+require 'micro/internet_connection'
+require 'micro/local_ip'
+require 'micro/monitored_process'
+require 'micro/monitored_process_group'
+require 'micro/monitored_process_groups'
+require 'micro/network_health'
+require 'micro/network_interface'
+require 'micro/shell_raiser'
+require 'micro/version'
View
@@ -0,0 +1,4 @@
+require 'micro/api/engine'
+require 'micro/api/media_type'
+require 'micro/api/route'
+require 'micro/api/server'
@@ -0,0 +1,5 @@
+require 'micro/api/engine/json_serializable'
+require 'micro/api/engine/link'
+require 'micro/api/engine/media_type'
+require 'micro/api/engine/media_type_serial'
+require 'micro/api/engine/expect_helper'
@@ -0,0 +1,28 @@
+module VCAP
+
+ module Micro
+
+ module Api
+
+ module Engine
+
+ module ExpectHelper
+
+ # Return HTTP 415 Unsupported media type unless the content
+ # type header matches the media type of the expected class.
+ def expect(*media_types)
+ unless media_types.map {
+ |mt| mt::MediaType }.include?(request.content_type)
+ halt 415
+ end
+ end
+
+ end
+
+ end
+
+ end
+
+ end
+
+end
@@ -0,0 +1,25 @@
+module VCAP
+
+ module Micro
+
+ module Api
+
+ module Engine
+
+ module JsonSerializable
+
+ # Convert to JSON by serializing a hash of the instance variables.
+ def to_json(*a)
+ Hash[instance_variables.map {
+ |iv| [iv[1..-1], instance_variable_get(iv)] }].to_json(*a)
+ end
+
+ end
+
+ end
+
+ end
+
+ end
+
+end
@@ -0,0 +1,30 @@
+module VCAP
+
+ module Micro
+
+ module Api
+
+ module Engine
+
+ # A link to another state in a hypermedia API.
+ class Link
+ include JsonSerializable
+
+ def initialize(fields={})
+ @method = fields[:method]
+ @href = fields[:href]
+ @type = fields[:type]
+ end
+
+ attr_accessor :method
+ attr_accessor :href
+ attr_accessor :type
+ end
+
+ end
+
+ end
+
+ end
+
+end
@@ -0,0 +1,104 @@
+require 'json'
+
+require 'graphviz'
+
+module VCAP
+
+ module Micro
+
+ module Api
+
+ module Engine
+
+ # Media type superclass.
+ #
+ # Subclass this class to create specific media types.
+ class MediaType
+ include JsonSerializable
+
+ def initialize(fields={})
+ @_links = {}
+ end
+
+ # Add a link.
+ def link(rel, href)
+ link_def = self.class::Links[rel]
+
+ unless link_def
+ raise "#{self.class} does not have link rel '#{rel}' defined"
+ end
+
+ @_links[rel] = {
+ :method => link_def[0],
+ :href => href,
+ :type => link_def[1]::MediaType
+ }
+
+ self
+ end
+
+ attr :_links
+
+ # Keep track of subclasses.
+ @subclasses = []
+
+ def self.inherited(child)
+ subclasses << child
+
+ # Create this on any subclasses so JSON.parse will find them.
+ child.instance_eval {
+
+ def json_create(o)
+ # Convert keys to symbols.
+ sym_data = Hash[o['data'].map { |k,v| [k.to_sym, v] }]
+
+ new sym_data
+ end
+
+ }
+ end
+
+ class << self;
+ attr_reader :subclasses
+ end
+
+ # Look up a subclass by its content type.
+ def self.from_content_type(content_type)
+ subclasses.find { |c| c::MediaType == content_type }
+ end
+
+ # Respond to each so every subclass can act as a Rack body.
+ def each
+ yield to_json
+ end
+
+ def self.graph
+ GraphViz::new(:G, :type => :digraph) do |g|
+ subclasses.inject({}) { |types,sc| add_to_graph(g, sc, types) }
+ end
+ end
+
+ def self.add_to_graph(graph, klass, types={})
+ return types if types[klass.name]
+
+ types[klass.name] = graph.add_nodes(klass.name.split('::').last)
+
+ klass::Links.each_pair do |rel,methodtype|
+ method, type = methodtype
+ add_to_graph(graph, type, types)
+ graph.add_edges(
+ types[klass.name], types[type.name])[:label] = rel
+ end
+
+ types
+ end
+
+ end
+
+ end
+
+ end
+
+ end
+
+end
Oops, something went wrong.

0 comments on commit 47c44d9

Please sign in to comment.