From 6fb1a4e496f6860c800f08e68c05b7e95be36c3b Mon Sep 17 00:00:00 2001
From: Dean Troyer <dtroyer@gmail.com>
Date: Wed, 9 May 2012 17:15:43 -0500
Subject: [PATCH] More identity client config

* move auth option checking back to OpenStackShell() to keep the shell-level
  interaction at that level; add checking for token flow options

* make identity.client.make_client() configure keystoneclient.v2_0.Client()
  properly for both password flow and token flow auth

* eliminated ClientManager.init_token(), set _service_catalog in __init__()

* compute client handles token flow

Change-Id: I42481b5424489387798c4ec6d3e2a723ab1e6067
---
 openstackclient/common/clientmanager.py | 43 +++++--------------------
 openstackclient/compute/client.py       | 11 +++++--
 openstackclient/identity/client.py      | 25 ++++++++------
 openstackclient/shell.py                | 36 +++++++++++++++++++++
 4 files changed, 68 insertions(+), 47 deletions(-)

diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index ab47cc5d54..04aef1dbed 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -21,7 +21,6 @@ class ClientCache(object):
     def __get__(self, instance, owner):
         # Tell the ClientManager to login to keystone
         if self._handle is None:
-            instance.init_token()
             self._handle = self.factory(instance)
         return self._handle
 
@@ -30,9 +29,7 @@ class ClientManager(object):
     """Manages access to API clients, including authentication.
     """
 
-    # Identity client is instantiated in init_token()
-    # otherwise we have a recursion problem
-    identity = None
+    identity = ClientCache(identity_client.make_client)
     compute = ClientCache(compute_client.make_client)
 
     def __init__(self, token=None, url=None,
@@ -55,40 +52,16 @@ class ClientManager(object):
         self._identity_api_version = identity_api_version
         self._compute_api_version = compute_api_version
         self._image_api_version = image_api_version
+        self._service_catalog = None
 
-    def init_token(self):
-        """Return the auth token and endpoint.
-        """
-        if self._token:
-            LOG.debug('using existing auth token')
-            return
+        # Create the identity client
+        self.identity
 
-        LOG.debug('validating authentication options')
-        if not self._username:
-            raise exc.CommandError(
-                "You must provide a username via"
-                " either --os-username or env[OS_USERNAME]")
+        if not self._url:
+            # Populate other password flow attributes
+            self._token = self.identity.auth_token
+            self._service_catalog = self.identity.service_catalog
 
-        if not self._password:
-            raise exc.CommandError(
-                "You must provide a password via"
-                " either --os-password or env[OS_PASSWORD]")
-
-        if not (self._tenant_id or self._tenant_name):
-            raise exc.CommandError(
-                "You must provide a tenant_id via"
-                " either --os-tenant-id or via env[OS_TENANT_ID]")
-
-        if not self._auth_url:
-            raise exc.CommandError(
-                "You must provide an auth url via"
-                " either --os-auth-url or via env[OS_AUTH_URL]")
-
-        # Get an Identity client and keep a token and catalog
-        if not self.identity:
-            self.identity = identity_client.make_client(self)
-        self._token = self.identity.auth_token
-        self._service_catalog = self.identity.service_catalog
         return
 
     def get_endpoint_for_service_type(self, service_type):
diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py
index 07f69aa103..30a724e02e 100644
--- a/openstackclient/compute/client.py
+++ b/openstackclient/compute/client.py
@@ -28,8 +28,13 @@ def make_client(instance):
         )
 
     # Populate the Nova client to skip another auth query to Identity
-    client.client.management_url = instance.get_endpoint_for_service_type(
-        'compute')
-    client.client.service_catalog = instance._service_catalog
+    if instance._url:
+        # token flow
+        client.client.management_url = instance._url
+    else:
+        # password flow
+        client.client.management_url = instance.get_endpoint_for_service_type(
+            'compute')
+        client.client.service_catalog = instance._service_catalog
     client.client.auth_token = instance._token
     return client
diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py
index be44098eb1..2d823d2bc9 100644
--- a/openstackclient/identity/client.py
+++ b/openstackclient/identity/client.py
@@ -8,13 +8,20 @@ LOG = logging.getLogger(__name__)
 def make_client(instance):
     """Returns an identity service client.
     """
-    LOG.debug('instantiating identity client')
-    client = identity_client.Client(
-        username=instance._username,
-        password=instance._password,
-        tenant_name=instance._tenant_name,
-        tenant_id=instance._tenant_id,
-        auth_url=instance._auth_url,
-        region_name=instance._region_name,
-    )
+    if instance._url:
+        LOG.debug('instantiating identity client: token flow')
+        client = identity_client.Client(
+            endpoint=instance._url,
+            token=instance._token,
+        )
+    else:
+        LOG.debug('instantiating identity client: password flow')
+        client = identity_client.Client(
+            username=instance._username,
+            password=instance._password,
+            tenant_name=instance._tenant_name,
+            tenant_id=instance._tenant_id,
+            auth_url=instance._auth_url,
+            region_name=instance._region_name,
+        )
     return client
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index fb5d07272b..83dd1040de 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -27,6 +27,7 @@ from cliff.app import App
 from cliff.commandmanager import CommandManager
 
 from openstackclient.common import clientmanager
+from openstackclient.common import exceptions as exc
 from openstackclient.common import utils
 
 
@@ -141,6 +142,41 @@ class OpenStackShell(App):
             'image': self.options.os_image_api_version,
         }
 
+        self.log.debug('validating authentication options')
+        if self.options.os_token or self.options.os_url:
+            # Token flow auth takes priority
+            if not self.options.os_token:
+                raise exc.CommandError(
+                    "You must provide a token via"
+                    " either --os-token or env[OS_TOKEN]")
+
+            if not self.options.os_url:
+                raise exc.CommandError(
+                    "You must provide a service URL via"
+                    " either --os-url or env[OS_URL]")
+
+        else:
+            # Validate password flow auth
+            if not self.options.os_username:
+                raise exc.CommandError(
+                    "You must provide a username via"
+                    " either --os-username or env[OS_USERNAME]")
+
+            if not self.options.os_password:
+                raise exc.CommandError(
+                    "You must provide a password via"
+                    " either --os-password or env[OS_PASSWORD]")
+
+            if not (self.options.os_tenant_id or self.options.os_tenant_name):
+                raise exc.CommandError(
+                    "You must provide a tenant_id via"
+                    " either --os-tenant-id or via env[OS_TENANT_ID]")
+
+            if not self.options.os_auth_url:
+                raise exc.CommandError(
+                    "You must provide an auth url via"
+                    " either --os-auth-url or via env[OS_AUTH_URL]")
+
         self.client_manager = clientmanager.ClientManager(
             token=self.options.os_token,
             url=self.options.os_url,