Skip to content

Instantly share code, notes, and snippets.

@wchrisjohnson
Last active August 29, 2015 13:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wchrisjohnson/5243c222504f7fab3572 to your computer and use it in GitHub Desktop.
Save wchrisjohnson/5243c222504f7fab3572 to your computer and use it in GitHub Desktop.
OpenStackCommon Versioning approach
# simple consumer
options = {
:provider => 'OpenStackCommon',
:openstack_auth_url => "http://devstack.local:5000/v2.0/tokens",
:openstack_username => "demo",
:openstack_api_key => "stack"
}
client = Fog::OpenStackCommon.authenticate(options)
# ... other client operations
# core.rb
module Fog
module OpenStackCommon
service(:identity, 'Identity')
# service(:compute , 'Compute')
# service(:image, 'Image')
#... rest of service definitions
def authenticate(options, connection_options = {})
version = Discovery.new('identity')
class_name = "Fog::Identity::V#{version}".constantize
class_name.send(new, options, connection_options)
end
end
end
# discovery.rb
# ------------
# Initially, this class will be used for identity, but no reason it shouldnt
# (eventually) be used for service/version discovery across all services in
# the catalog.
module Fog
module OpenStackCommon
class Discovery
# -- params --
# service identifier (used to look up service in catalog), required
# service url, optional
# service version, optional, but can be stipulated if desired
def initialize(params = {})
# Use the version parameter, if available
# Use the version embedded in the url, if available
# Use the latest stable version available in the service catalog
end
end
end
end
# identity_v2.rb
module Fog
module Identity
module V2
class OpenStackCommon < Fog::Service
requires :openstack_auth_url
recognizes :openstack_auth_token, :openstack_management_url, :persistent,
:openstack_service_type, :openstack_service_name, :openstack_tenant,
:openstack_api_key, :openstack_username, :openstack_current_user_id,
:openstack_endpoint_type,
:current_user, :current_tenant
model_path 'fog/openstackcommon/models/identity/v2'
model :tenant
collection :tenants
# ..... other models and collections
request_path 'fog/openstackcommon/requests/identity/v2'
## EC2 Credentials
request :list_ec2_credentials
# ... other v2 requests
def initialize(options={})
authenticate
end
private
def authenticate
# v2 specific auth logic - no need for adapter to vary between versions
end
end
end
end
end
# identity_v3.rb
module Fog
module Identity
module V3
class OpenStackCommon < Fog::Service
requires :openstack_auth_url
recognizes :openstack_auth_token, :openstack_management_url, :persistent,
:openstack_service_type, :openstack_service_name, :openstack_tenant,
:openstack_api_key, :openstack_username, :openstack_current_user_id,
:openstack_endpoint_type,
:current_user, :current_tenant
model_path 'fog/openstackcommon/models/identity/v3'
model :project
collection :projects
# ..... other models and collections
request_path 'fog/openstackcommon/requests/identity/v3'
## domains
request :list_projects
# ... other v3 requests
def initialize(options={})
authenticate
end
private
def authenticate
# v3 specific auth logic - no need for adapter to vary between versions
end
end
end
end
end
@elight
Copy link

elight commented Mar 11, 2014

How about, instead of this we do something a little more like this.

Though I wonder if the Authenticators shouldn't just respond to call instead of an authenticate method. That's more idiomatic for Ruby objects that respond to a single method.

@elight
Copy link

elight commented Mar 11, 2014

@krames and I discussed something a little like this earlier today. We believe it poses a problem because it differs significantly from the "Fog way". Though I agree that this is better.

@wchrisjohnson
Copy link
Author

To your first comment, if we go with the approach in the gist above, then the adapter used to decide what authenticator goes away entirely. Each version of Identity has its own authenticate (lines 94, 138) method specific to that version. I would probably still have them exist in their own class as opposed to living inside the Identity class itself - simpler to test/spec, more transparent, less coupled.

The bigger win, IMO is that if we do this sort of discovery approach, initially with Identity, but then later with the other services, we gain the ability to abstract away the need for the user to specify a version for each service. If an engineer needs to spin up a specific version they will be able to do so, but by default, the provider is smart about choosing the correct version. This is the approach that is currently being adopted in the python sdk community. And it couldn't be a more timely discussion for us given this refactor and the summit in May.

Ultimately, the discovery class would get pulled out to a top level class for the sake of re-use.

To your second comment, I think that deserves to be on the list for discussion in May. ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment