Merge "Use API v2 as default"
This commit is contained in:
commit
13af690396
|
@ -22,6 +22,7 @@ from __future__ import print_function
|
|||
import argparse
|
||||
import copy
|
||||
import getpass
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
@ -263,7 +264,7 @@ class OpenStackImagesShell(object):
|
|||
parser.add_argument('--os-image-api-version',
|
||||
default=utils.env('OS_IMAGE_API_VERSION',
|
||||
default=None),
|
||||
help='Defaults to env[OS_IMAGE_API_VERSION] or 1.')
|
||||
help='Defaults to env[OS_IMAGE_API_VERSION] or 2.')
|
||||
|
||||
parser.add_argument('--os_image_api_version',
|
||||
help=argparse.SUPPRESS)
|
||||
|
@ -551,9 +552,13 @@ class OpenStackImagesShell(object):
|
|||
|
||||
def _cache_schemas(self, options, home_dir='~/.glanceclient'):
|
||||
homedir = os.path.expanduser(home_dir)
|
||||
if not os.path.exists(homedir):
|
||||
path_prefix = homedir
|
||||
if options.os_auth_url:
|
||||
hash_host = hashlib.sha1(options.os_auth_url.encode('utf-8'))
|
||||
path_prefix = os.path.join(path_prefix, hash_host.hexdigest())
|
||||
if not os.path.exists(path_prefix):
|
||||
try:
|
||||
os.makedirs(homedir)
|
||||
os.makedirs(path_prefix)
|
||||
except OSError as e:
|
||||
# This avoids glanceclient to crash if it can't write to
|
||||
# ~/.glanceclient, which may happen on some env (for me,
|
||||
|
@ -561,12 +566,12 @@ class OpenStackImagesShell(object):
|
|||
# /var/lib/jenkins).
|
||||
msg = '%s' % e
|
||||
print(encodeutils.safe_decode(msg), file=sys.stderr)
|
||||
|
||||
resources = ['image', 'metadefs/namespace', 'metadefs/resource_type']
|
||||
schema_file_paths = [homedir + os.sep + x + '_schema.json'
|
||||
schema_file_paths = [os.path.join(path_prefix, x + '_schema.json')
|
||||
for x in ['image', 'namespace', 'resource_type']]
|
||||
|
||||
client = None
|
||||
failed_download_schema = 0
|
||||
for resource, schema_file_path in zip(resources, schema_file_paths):
|
||||
if (not os.path.exists(schema_file_path)) or options.get_schema:
|
||||
try:
|
||||
|
@ -580,8 +585,11 @@ class OpenStackImagesShell(object):
|
|||
except Exception:
|
||||
# NOTE(esheffield) do nothing here, we'll get a message
|
||||
# later if the schema is missing
|
||||
failed_download_schema += 1
|
||||
pass
|
||||
|
||||
return failed_download_schema >= len(resources)
|
||||
|
||||
def main(self, argv):
|
||||
# Parse args once to find version
|
||||
|
||||
|
@ -605,7 +613,7 @@ class OpenStackImagesShell(object):
|
|||
|
||||
# build available subcommands based on version
|
||||
try:
|
||||
api_version = int(options.os_image_api_version or url_version or 1)
|
||||
api_version = int(options.os_image_api_version or url_version or 2)
|
||||
if api_version not in SUPPORTED_VERSIONS:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
|
@ -614,7 +622,12 @@ class OpenStackImagesShell(object):
|
|||
utils.exit(msg=msg)
|
||||
|
||||
if api_version == 2:
|
||||
self._cache_schemas(options)
|
||||
switch_version = self._cache_schemas(options)
|
||||
if switch_version:
|
||||
print('WARNING: The client is falling back to v1 because'
|
||||
' the accessing to v2 failed. This behavior will'
|
||||
' be removed in future versions')
|
||||
api_version = 1
|
||||
|
||||
try:
|
||||
subcommand_parser = self.get_subcommand_parser(api_version)
|
||||
|
|
|
@ -24,26 +24,61 @@ class SimpleReadOnlyGlanceClientTest(base.ClientTestBase):
|
|||
This only exercises client commands that are read only.
|
||||
"""
|
||||
|
||||
def test_list(self):
|
||||
out = self.glance('image-list')
|
||||
def test_list_v1(self):
|
||||
out = self.glance('--os-image-api-version 1 image-list')
|
||||
endpoints = self.parser.listing(out)
|
||||
self.assertTableStruct(endpoints, [
|
||||
'ID', 'Name', 'Disk Format', 'Container Format',
|
||||
'Size', 'Status'])
|
||||
|
||||
def test_list_v2(self):
|
||||
out = self.glance('--os-image-api-version 2 image-list')
|
||||
endpoints = self.parser.listing(out)
|
||||
self.assertTableStruct(endpoints, ['ID', 'Name'])
|
||||
|
||||
def test_fake_action(self):
|
||||
self.assertRaises(exceptions.CommandFailed,
|
||||
self.glance,
|
||||
'this-does-not-exist')
|
||||
|
||||
def test_member_list(self):
|
||||
def test_member_list_v1(self):
|
||||
tenant_name = '--tenant-id %s' % self.tenant_name
|
||||
out = self.glance('member-list',
|
||||
out = self.glance('--os-image-api-version 1 member-list',
|
||||
params=tenant_name)
|
||||
endpoints = self.parser.listing(out)
|
||||
self.assertTableStruct(endpoints,
|
||||
['Image ID', 'Member ID', 'Can Share'])
|
||||
|
||||
def test_member_list_v2(self):
|
||||
try:
|
||||
# NOTE(flwang): If set disk-format and container-format, Jenkins
|
||||
# will raise an error said can't recognize the params, thouhg it
|
||||
# works fine at local. Without the two params, Glance will
|
||||
# complain. So we just catch the exception can skip it.
|
||||
self.glance('--os-image-api-version 2 image-create --name temp')
|
||||
except Exception:
|
||||
pass
|
||||
out = self.glance('--os-image-api-version 2 image-list'
|
||||
' --visibility private')
|
||||
image_list = self.parser.listing(out)
|
||||
# NOTE(flwang): Because the member-list command of v2 is using
|
||||
# image-id as required parameter, so we have to get a valid image id
|
||||
# based on current environment. If there is no valid image id, we will
|
||||
# pass in a fake one and expect a 404 error.
|
||||
if len(image_list) > 0:
|
||||
param_image_id = '--image-id %s' % image_list[0]['ID']
|
||||
out = self.glance('--os-image-api-version 2 member-list',
|
||||
params=param_image_id)
|
||||
endpoints = self.parser.listing(out)
|
||||
self.assertTableStruct(endpoints,
|
||||
['Image ID', 'Member ID', 'Status'])
|
||||
else:
|
||||
param_image_id = '--image-id fake_image_id'
|
||||
self.assertRaises(exceptions.CommandFailed,
|
||||
self.glance,
|
||||
'--os-image-api-version 2 member-list',
|
||||
params=param_image_id)
|
||||
|
||||
def test_help(self):
|
||||
help_text = self.glance('help')
|
||||
lines = help_text.split('\n')
|
||||
|
|
|
@ -19,6 +19,7 @@ try:
|
|||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from ordereddict import OrderedDict
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
|
@ -204,8 +205,8 @@ class ShellTest(testutils.TestCase):
|
|||
def test_no_auth_with_token_and_image_url_with_v1(self, v1_client):
|
||||
# test no authentication is required if both token and endpoint url
|
||||
# are specified
|
||||
args = ('--os-auth-token mytoken --os-image-url https://image:1234/v1 '
|
||||
'image-list')
|
||||
args = ('--os-image-api-version 1 --os-auth-token mytoken'
|
||||
' --os-image-url https://image:1234/v1 image-list')
|
||||
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||
glance_shell.main(args.split())
|
||||
assert v1_client.called
|
||||
|
@ -213,7 +214,8 @@ class ShellTest(testutils.TestCase):
|
|||
self.assertEqual('mytoken', kwargs['token'])
|
||||
self.assertEqual('https://image:1234', args[0])
|
||||
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
|
||||
return_value=False)
|
||||
def test_no_auth_with_token_and_image_url_with_v2(self,
|
||||
cache_schemas):
|
||||
with mock.patch('glanceclient.v2.client.Client') as v2_client:
|
||||
|
@ -244,13 +246,14 @@ class ShellTest(testutils.TestCase):
|
|||
|
||||
@mock.patch('glanceclient.v1.client.Client')
|
||||
def test_auth_plugin_invocation_with_v1(self, v1_client):
|
||||
args = 'image-list'
|
||||
args = '--os-image-api-version 1 image-list'
|
||||
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||
glance_shell.main(args.split())
|
||||
self._assert_auth_plugin_args()
|
||||
|
||||
@mock.patch('glanceclient.v2.client.Client')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
|
||||
return_value=False)
|
||||
def test_auth_plugin_invocation_with_v2(self,
|
||||
v2_client,
|
||||
cache_schemas):
|
||||
|
@ -262,13 +265,15 @@ class ShellTest(testutils.TestCase):
|
|||
@mock.patch('glanceclient.v1.client.Client')
|
||||
def test_auth_plugin_invocation_with_unversioned_auth_url_with_v1(
|
||||
self, v1_client):
|
||||
args = '--os-auth-url %s image-list' % DEFAULT_UNVERSIONED_AUTH_URL
|
||||
args = ('--os-image-api-version 1 --os-auth-url %s image-list' %
|
||||
DEFAULT_UNVERSIONED_AUTH_URL)
|
||||
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||
glance_shell.main(args.split())
|
||||
self._assert_auth_plugin_args()
|
||||
|
||||
@mock.patch('glanceclient.v2.client.Client')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
|
||||
return_value=False)
|
||||
def test_auth_plugin_invocation_with_unversioned_auth_url_with_v2(
|
||||
self, v2_client, cache_schemas):
|
||||
args = ('--os-auth-url %s --os-image-api-version 2 '
|
||||
|
@ -300,7 +305,8 @@ class ShellTest(testutils.TestCase):
|
|||
|
||||
@mock.patch(
|
||||
'glanceclient.shell.OpenStackImagesShell._get_keystone_session')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
|
||||
return_value=False)
|
||||
def test_no_auth_with_proj_name(self, cache_schemas, session):
|
||||
with mock.patch('glanceclient.v2.client.Client'):
|
||||
args = ('--os-project-name myname '
|
||||
|
@ -414,7 +420,10 @@ class ShellTest(testutils.TestCase):
|
|||
self.assertRaises(exc.CommandError, glance_shell.main, args.split())
|
||||
|
||||
@mock.patch('glanceclient.v2.client.Client')
|
||||
def test_auth_plugin_invocation_without_tenant_with_v2(self, v2_client):
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
|
||||
return_value=False)
|
||||
def test_auth_plugin_invocation_without_tenant_with_v2(self, v2_client,
|
||||
cache_schemas):
|
||||
if 'OS_TENANT_NAME' in os.environ:
|
||||
self.make_env(exclude='OS_TENANT_NAME')
|
||||
if 'OS_PROJECT_ID' in os.environ:
|
||||
|
@ -445,13 +454,14 @@ class ShellTestWithKeystoneV3Auth(ShellTest):
|
|||
|
||||
@mock.patch('glanceclient.v1.client.Client')
|
||||
def test_auth_plugin_invocation_with_v1(self, v1_client):
|
||||
args = 'image-list'
|
||||
args = '--os-image-api-version 1 image-list'
|
||||
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||
glance_shell.main(args.split())
|
||||
self._assert_auth_plugin_args()
|
||||
|
||||
@mock.patch('glanceclient.v2.client.Client')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas')
|
||||
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schemas',
|
||||
return_value=False)
|
||||
def test_auth_plugin_invocation_with_v2(self, v2_client, cache_schemas):
|
||||
args = '--os-image-api-version 2 image-list'
|
||||
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||
|
@ -489,9 +499,12 @@ class ShellCacheSchemaTest(testutils.TestCase):
|
|||
self._mock_client_setup()
|
||||
self._mock_shell_setup()
|
||||
self.cache_dir = '/dir_for_cached_schema'
|
||||
self.cache_files = [self.cache_dir + '/image_schema.json',
|
||||
self.cache_dir + '/namespace_schema.json',
|
||||
self.cache_dir + '/resource_type_schema.json']
|
||||
self.os_auth_url = 'http://localhost:5000/v2'
|
||||
url_hex = hashlib.sha1(self.os_auth_url.encode('utf-8')).hexdigest()
|
||||
self.prefix_path = (self.cache_dir + '/' + url_hex)
|
||||
self.cache_files = [self.prefix_path + '/image_schema.json',
|
||||
self.prefix_path + '/namespace_schema.json',
|
||||
self.prefix_path + '/resource_type_schema.json']
|
||||
|
||||
def tearDown(self):
|
||||
super(ShellCacheSchemaTest, self).tearDown()
|
||||
|
@ -524,7 +537,8 @@ class ShellCacheSchemaTest(testutils.TestCase):
|
|||
@mock.patch('os.path.exists', return_value=True)
|
||||
def test_cache_schemas_gets_when_forced(self, exists_mock):
|
||||
options = {
|
||||
'get_schema': True
|
||||
'get_schema': True,
|
||||
'os_auth_url': self.os_auth_url
|
||||
}
|
||||
schema_odict = OrderedDict(self.schema_dict)
|
||||
|
||||
|
@ -545,7 +559,8 @@ class ShellCacheSchemaTest(testutils.TestCase):
|
|||
@mock.patch('os.path.exists', side_effect=[True, False, False, False])
|
||||
def test_cache_schemas_gets_when_not_exists(self, exists_mock):
|
||||
options = {
|
||||
'get_schema': False
|
||||
'get_schema': False,
|
||||
'os_auth_url': self.os_auth_url
|
||||
}
|
||||
schema_odict = OrderedDict(self.schema_dict)
|
||||
|
||||
|
@ -566,14 +581,29 @@ class ShellCacheSchemaTest(testutils.TestCase):
|
|||
@mock.patch('os.path.exists', return_value=True)
|
||||
def test_cache_schemas_leaves_when_present_not_forced(self, exists_mock):
|
||||
options = {
|
||||
'get_schema': False
|
||||
'get_schema': False,
|
||||
'os_auth_url': self.os_auth_url
|
||||
}
|
||||
|
||||
self.shell._cache_schemas(self._make_args(options),
|
||||
home_dir=self.cache_dir)
|
||||
|
||||
os.path.exists.assert_any_call(self.cache_dir)
|
||||
os.path.exists.assert_any_call(self.prefix_path)
|
||||
os.path.exists.assert_any_call(self.cache_files[0])
|
||||
os.path.exists.assert_any_call(self.cache_files[1])
|
||||
self.assertEqual(4, exists_mock.call_count)
|
||||
self.assertEqual(0, open.mock_calls.__len__())
|
||||
|
||||
@mock.patch('six.moves.builtins.open', new=mock.mock_open(), create=True)
|
||||
@mock.patch('os.path.exists', return_value=True)
|
||||
def test_cache_schemas_leaves_auto_switch(self, exists_mock):
|
||||
options = {
|
||||
'get_schema': True,
|
||||
'os_auth_url': self.os_auth_url
|
||||
}
|
||||
|
||||
self.client.schemas.get.return_value = Exception()
|
||||
|
||||
switch_version = self.shell._cache_schemas(self._make_args(options),
|
||||
home_dir=self.cache_dir)
|
||||
self.assertEqual(switch_version, True)
|
||||
|
|
Loading…
Reference in New Issue