Changes versioned URIs to be /v1/ instead of /v1.0/

Adds middleware that detects versioned URIs and also
detects media types in the Accept: header and attempts
to determine the API controller to return for the
client request.

Adds a bunch of functional test cases for variations
of calling the versioned and unversioned URIs with and
without Accept: headers.
This commit is contained in:
jaypipes@gmail.com
2011-05-11 19:03:51 -04:00
parent 7f6944c816
commit 77054d402c
14 changed files with 329 additions and 67 deletions

View File

@@ -51,19 +51,17 @@ swift_store_container = glance
# Do we create the container if it does not exist? # Do we create the container if it does not exist?
swift_store_create_container_on_put = False swift_store_create_container_on_put = False
[composite:glance-api] [pipeline:glance-api]
use = egg:Paste#urlmap pipeline = versionnegotiation apiv1app
/: versions
/v1.0: api_1_0
[pipeline:api_1_0]
pipeline = api_1_0_app
[pipeline:versions] [pipeline:versions]
pipeline = versions_app pipeline = versionsapp
[app:versions_app] [app:versionsapp]
paste.app_factory = glance.api.versions:app_factory paste.app_factory = glance.api.versions:app_factory
[app:api_1_0_app] [app:apiv1app]
paste.app_factory = glance.api.v1_0:app_factory paste.app_factory = glance.api.v1:app_factory
[filter:versionnegotiation]
paste.filter_factory = glance.api.middleware.version_negotiation:filter_factory

View File

@@ -0,0 +1,16 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

View File

@@ -0,0 +1,118 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
A filter middleware that inspects the requested URI for a version string
and/or Accept headers and attempts to negotiate an API controller to
return
"""
import logging
import re
import routes
from glance.api import v1
from glance.api import versions
from glance.common import wsgi
logger = logging.getLogger('glance.api.middleware.version_negotiation')
class VersionNegotiationFilter(wsgi.Middleware):
def __init__(self, app, options):
self.versions_app = versions.Controller(options)
self.version_uri_regex = re.compile(r"^v(\d+)\.?(\d+)?")
self.options = options
super(VersionNegotiationFilter, self).__init__(app)
def process_request(self, req):
"""
If there is a version identifier in the URI, simply
return the correct API controller, otherwise, if we
find an Accept: header, process it
"""
# See if a version identifier is in the URI passed to
# us already. If so, simply return the right version
# API controller
logger.debug("Processing request: %s %s Accept: %s",
req.method, req.path, req.accept)
match = self._match_version_string(req.path_info_peek(), req)
if match:
logger.debug("Matched versioned URI. Version: %d.%d",
req.environ['api.major_version'],
req.environ['api.minor_version'])
if req.environ['api.major_version'] == 1:
# Strip the version from the path
req.path_info_pop()
return None
else:
return self.versions_app
accept = req.headers['Accept']
if accept.startswith('application/vnd.openstack.images'):
token_loc = len('application/vnd.openstack.images')
accept_version = accept[token_loc:]
match = self._match_version_string(accept_version, req)
if match:
logger.debug("Matched versioned media type. Version: %d.%d",
req.environ['api.major_version'],
req.environ['api.minor_version'])
if req.environ['api.major_version'] == 1:
return None
else:
return self.versions_app
else:
if req.accept not in ('*/*', ''):
logger.debug("Unknown accept header: %s..."
"returning version choices.", req.accept)
return self.versions_app
return None
def _match_version_string(self, subject, req):
"""
Given a subject string, tries to match a major and/or
minor version number. If found, sets the api.major_version
and api.minor_version environ variables.
Returns True if there was a match, false otherwise.
:param subject: The string to check
:param req: Webob.Request object
"""
match = self.version_uri_regex.match(subject)
if match:
major_version, minor_version = match.groups(0)
major_version = int(major_version)
minor_version = int(minor_version)
req.environ['api.major_version'] = major_version
req.environ['api.minor_version'] = minor_version
return match is not None
def filter_factory(global_conf, **local_conf):
"""
Factory method for paste.deploy
"""
conf = global_conf.copy()
conf.update(local_conf)
def filter(app):
return VersionNegotiationFilter(app, conf)
return filter

View File

@@ -19,15 +19,15 @@ import logging
import routes import routes
from glance.api.v1_0 import images from glance.api.v1 import images
from glance.common import wsgi from glance.common import wsgi
logger = logging.getLogger('glance.api.v1_0') logger = logging.getLogger('glance.api.v1')
class API(wsgi.Router): class API(wsgi.Router):
"""WSGI router for Glance v1.0 API requests.""" """WSGI router for Glance v1 API requests."""
def __init__(self, options): def __init__(self, options):
self.options = options self.options = options

View File

@@ -16,7 +16,7 @@
# under the License. # under the License.
""" """
/images endpoint for Glance v1.0 API /images endpoint for Glance v1 API
""" """
import httplib import httplib
@@ -40,13 +40,13 @@ from glance import registry
from glance import utils from glance import utils
logger = logging.getLogger('glance.api.v1_0.images') logger = logging.getLogger('glance.api.v1.images')
class Controller(wsgi.Controller): class Controller(wsgi.Controller):
""" """
WSGI controller for images resource in Glance v1.0 API WSGI controller for images resource in Glance v1 API
The images resource API is a RESTful web service for image data. The API The images resource API is a RESTful web service for image data. The API
is as follows:: is as follows::
@@ -131,7 +131,7 @@ class Controller(wsgi.Controller):
res = Response(request=req) res = Response(request=req)
utils.inject_image_meta_into_headers(res, image) utils.inject_image_meta_into_headers(res, image)
res.headers.add('Location', "/v1.0/images/%s" % id) res.headers.add('Location', "/v1/images/%s" % id)
res.headers.add('ETag', image['checksum']) res.headers.add('ETag', image['checksum'])
return req.get_response(res) return req.get_response(res)
@@ -164,7 +164,7 @@ class Controller(wsgi.Controller):
# Using app_iter blanks content-length, so we set it here... # Using app_iter blanks content-length, so we set it here...
res.headers.add('Content-Length', image['size']) res.headers.add('Content-Length', image['size'])
utils.inject_image_meta_into_headers(res, image) utils.inject_image_meta_into_headers(res, image)
res.headers.add('Location', "/v1.0/images/%s" % id) res.headers.add('Location', "/v1/images/%s" % id)
res.headers.add('ETag', image['checksum']) res.headers.add('ETag', image['checksum'])
return req.get_response(res) return req.get_response(res)
@@ -386,7 +386,7 @@ class Controller(wsgi.Controller):
# URI of the resource newly-created. # URI of the resource newly-created.
res = Response(request=req, body=json.dumps(dict(image=image_meta)), res = Response(request=req, body=json.dumps(dict(image=image_meta)),
status=httplib.CREATED, content_type="text/plain") status=httplib.CREATED, content_type="text/plain")
res.headers.add('Location', "/v1.0/images/%s" % image_id) res.headers.add('Location', "/v1/images/%s" % image_id)
res.headers.add('ETag', image_meta['checksum']) res.headers.add('ETag', image_meta['checksum'])
return req.get_response(res) return req.get_response(res)

View File

@@ -56,7 +56,7 @@ class Controller(object):
return response return response
def get_href(self): def get_href(self):
return "http://%s:%s/v1.0" % (self.options['bind_host'], return "http://%s:%s/v1/" % (self.options['bind_host'],
self.options['bind_port']) self.options['bind_port'])

View File

@@ -177,13 +177,13 @@ class BaseClient(object):
return response.status return response.status
class V1_0_Client(BaseClient): class V1Client(BaseClient):
"""Main client class for accessing Glance resources""" """Main client class for accessing Glance resources"""
DEFAULT_PORT = 9292 DEFAULT_PORT = 9292
def __init__(self, host, port=None, use_ssl=False, doc_root="/v1.0"): def __init__(self, host, port=None, use_ssl=False, doc_root="/v1"):
""" """
Creates a new client to a Glance API service. Creates a new client to a Glance API service.
@@ -199,7 +199,7 @@ class V1_0_Client(BaseClient):
def do_request(self, method, action, body=None, headers=None): def do_request(self, method, action, body=None, headers=None):
action = "%s/%s" % (self.doc_root, action.lstrip("/")) action = "%s/%s" % (self.doc_root, action.lstrip("/"))
return super(V1_0_Client, self).do_request(method, action, return super(V1Client, self).do_request(method, action,
body, headers) body, headers)
def get_images(self): def get_images(self):
@@ -297,4 +297,4 @@ class V1_0_Client(BaseClient):
return True return True
Client = V1_0_Client Client = V1Client

View File

@@ -148,22 +148,20 @@ registry_host = 0.0.0.0
registry_port = %(registry_port)s registry_port = %(registry_port)s
log_file = %(log_file)s log_file = %(log_file)s
[composite:glance-api] [pipeline:glance-api]
use = egg:Paste#urlmap pipeline = versionnegotiation apiv1app
/: versions
/v1.0: api_1_0
[pipeline:api_1_0]
pipeline = api_1_0_app
[pipeline:versions] [pipeline:versions]
pipeline = versions_app pipeline = versionsapp
[app:versions_app] [app:versionsapp]
paste.app_factory = glance.api.versions:app_factory paste.app_factory = glance.api.versions:app_factory
[app:api_1_0_app] [app:apiv1app]
paste.app_factory = glance.api.v1_0:app_factory paste.app_factory = glance.api.v1:app_factory
[filter:versionnegotiation]
paste.filter_factory = glance.api.middleware.version_negotiation:filter_factory
""" """

View File

@@ -72,7 +72,7 @@ class TestCurlApi(functional.FunctionalTest):
# 0. GET /images # 0. GET /images
# Verify no public images # Verify no public images
cmd = "curl -g http://0.0.0.0:%d/v1.0/images" % api_port cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -81,7 +81,7 @@ class TestCurlApi(functional.FunctionalTest):
# 1. GET /images/detail # 1. GET /images/detail
# Verify no public images # Verify no public images
cmd = "curl -g http://0.0.0.0:%d/v1.0/images/detail" % api_port cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -90,7 +90,7 @@ class TestCurlApi(functional.FunctionalTest):
# 2. HEAD /images/1 # 2. HEAD /images/1
# Verify 404 returned # Verify 404 returned
cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1.0/images/1" % api_port cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1/images/1" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -111,7 +111,7 @@ class TestCurlApi(functional.FunctionalTest):
"-H 'X-Image-Meta-Name: Image1' " "-H 'X-Image-Meta-Name: Image1' "
"-H 'X-Image-Meta-Is-Public: True' " "-H 'X-Image-Meta-Is-Public: True' "
"--data-binary \"%s\" " "--data-binary \"%s\" "
"http://0.0.0.0:%d/v1.0/images") % (image_data, api_port) "http://0.0.0.0:%d/v1/images") % (image_data, api_port)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
@@ -123,7 +123,7 @@ class TestCurlApi(functional.FunctionalTest):
# 4. HEAD /images # 4. HEAD /images
# Verify image found now # Verify image found now
cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1.0/images/1" % api_port cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1/images/1" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -138,7 +138,7 @@ class TestCurlApi(functional.FunctionalTest):
# 5. GET /images/1 # 5. GET /images/1
# Verify all information on image we just added is correct # Verify all information on image we just added is correct
cmd = "curl -i -g http://0.0.0.0:%d/v1.0/images/1" % api_port cmd = "curl -i http://0.0.0.0:%d/v1/images/1" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -212,7 +212,7 @@ class TestCurlApi(functional.FunctionalTest):
# 6. GET /images # 6. GET /images
# Verify no public images # Verify no public images
cmd = "curl -g http://0.0.0.0:%d/v1.0/images" % api_port cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -229,7 +229,7 @@ class TestCurlApi(functional.FunctionalTest):
# 7. GET /images/detail # 7. GET /images/detail
# Verify image and all its metadata # Verify image and all its metadata
cmd = "curl -g http://0.0.0.0:%d/v1.0/images/detail" % api_port cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -266,7 +266,7 @@ class TestCurlApi(functional.FunctionalTest):
cmd = ("curl -i -X PUT " cmd = ("curl -i -X PUT "
"-H 'X-Image-Meta-Property-Distro: Ubuntu' " "-H 'X-Image-Meta-Property-Distro: Ubuntu' "
"-H 'X-Image-Meta-Property-Arch: x86_64' " "-H 'X-Image-Meta-Property-Arch: x86_64' "
"http://0.0.0.0:%d/v1.0/images/1") % api_port "http://0.0.0.0:%d/v1/images/1") % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
@@ -278,7 +278,7 @@ class TestCurlApi(functional.FunctionalTest):
# 9. GET /images/detail # 9. GET /images/detail
# Verify image and all its metadata # Verify image and all its metadata
cmd = "curl -g http://0.0.0.0:%d/v1.0/images/detail" % api_port cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -312,7 +312,7 @@ class TestCurlApi(functional.FunctionalTest):
# 10. PUT /images/1 and remove a previously existing property. # 10. PUT /images/1 and remove a previously existing property.
cmd = ("curl -i -X PUT " cmd = ("curl -i -X PUT "
"-H 'X-Image-Meta-Property-Arch: x86_64' " "-H 'X-Image-Meta-Property-Arch: x86_64' "
"http://0.0.0.0:%d/v1.0/images/1") % api_port "http://0.0.0.0:%d/v1/images/1") % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
@@ -322,7 +322,7 @@ class TestCurlApi(functional.FunctionalTest):
self.assertEqual("HTTP/1.1 200 OK", status_line) self.assertEqual("HTTP/1.1 200 OK", status_line)
cmd = "curl -g http://0.0.0.0:%d/v1.0/images/detail" % api_port cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -336,7 +336,7 @@ class TestCurlApi(functional.FunctionalTest):
cmd = ("curl -i -X PUT " cmd = ("curl -i -X PUT "
"-H 'X-Image-Meta-Property-Distro: Ubuntu' " "-H 'X-Image-Meta-Property-Distro: Ubuntu' "
"-H 'X-Image-Meta-Property-Arch: x86_64' " "-H 'X-Image-Meta-Property-Arch: x86_64' "
"http://0.0.0.0:%d/v1.0/images/1") % api_port "http://0.0.0.0:%d/v1/images/1") % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
@@ -346,7 +346,7 @@ class TestCurlApi(functional.FunctionalTest):
self.assertEqual("HTTP/1.1 200 OK", status_line) self.assertEqual("HTTP/1.1 200 OK", status_line)
cmd = "curl -g http://0.0.0.0:%d/v1.0/images/detail" % api_port cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -391,7 +391,7 @@ class TestCurlApi(functional.FunctionalTest):
# 0. GET /images # 0. GET /images
# Verify no public images # Verify no public images
cmd = "curl -g http://0.0.0.0:%d/v1.0/images" % api_port cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -405,7 +405,7 @@ class TestCurlApi(functional.FunctionalTest):
"-H 'Expect: ' " # Necessary otherwise sends 100 Continue "-H 'Expect: ' " # Necessary otherwise sends 100 Continue
"-H 'X-Image-Meta-Name: Image1' " "-H 'X-Image-Meta-Name: Image1' "
"-H 'X-Image-Meta-Is-Public: True' " "-H 'X-Image-Meta-Is-Public: True' "
"http://0.0.0.0:%d/v1.0/images") % api_port "http://0.0.0.0:%d/v1/images") % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
@@ -417,7 +417,7 @@ class TestCurlApi(functional.FunctionalTest):
# 2. GET /images # 2. GET /images
# Verify 1 public image # Verify 1 public image
cmd = "curl -g http://0.0.0.0:%d/v1.0/images" % api_port cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -433,7 +433,7 @@ class TestCurlApi(functional.FunctionalTest):
# 3. HEAD /images # 3. HEAD /images
# Verify status is in queued # Verify status is in queued
cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1.0/images/1" % api_port cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1/images/1" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -453,7 +453,7 @@ class TestCurlApi(functional.FunctionalTest):
"-H 'Expect: ' " # Necessary otherwise sends 100 Continue "-H 'Expect: ' " # Necessary otherwise sends 100 Continue
"-H 'Content-Type: application/octet-stream' " "-H 'Content-Type: application/octet-stream' "
"--data-binary \"%s\" " "--data-binary \"%s\" "
"http://0.0.0.0:%d/v1.0/images/1") % (image_data, api_port) "http://0.0.0.0:%d/v1/images/1") % (image_data, api_port)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
@@ -465,7 +465,7 @@ class TestCurlApi(functional.FunctionalTest):
# 5. HEAD /images # 5. HEAD /images
# Verify status is in active # Verify status is in active
cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1.0/images/1" % api_port cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1/images/1" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -480,7 +480,7 @@ class TestCurlApi(functional.FunctionalTest):
# 6. GET /images # 6. GET /images
# Verify 1 public image still... # Verify 1 public image still...
cmd = "curl -g http://0.0.0.0:%d/v1.0/images" % api_port cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -494,6 +494,139 @@ class TestCurlApi(functional.FunctionalTest):
"size": 5120} "size": 5120}
self.assertEqual(expected, image) self.assertEqual(expected, image)
def test_version_variations(self):
"""
We test that various calls to the images and root endpoints are
handled properly, and that usage of the Accept: header does
content negotiation properly.
0. GET / with no Accept: header
Verify version choices returned.
1. GET /images with no Accept: header
Verify version choices returned.
2. GET /v1/images with no Accept: header
Verify empty image list returned.
3. GET / with an Accept: unknown header
Verify version choices returned. Verify message in API log about
unknown accept header.
4. GET / with an Accept: application/vnd.openstack.images-v1
Verify empty image list returned
5. GET /images with a Accept: application/vnd.openstack.compute-v1
header. Verify version choices returned. Verify message in API log
about unknown accept header.
6. GET /v1.0/images with no Accept: header
Verify empty image list returned
7. GET /v1.a/images with no Accept: header
Verify empty image list returned
8. GET /va.1/images with no Accept: header
Verify version choices returned.
"""
self.cleanup()
self.start_servers()
api_port = self.api_port
registry_port = self.registry_port
versions = {'versions': [{
"id": "v1.0",
"status": "CURRENT",
"links": [{
"rel": "self",
"href": "http://0.0.0.0:%d/v1/" % api_port}]}]}
versions_json = json.dumps(versions)
images = {'images': []}
images_json = json.dumps(images)
# 0. GET / with no Accept: header
# Verify version choices returned.
cmd = "curl http://0.0.0.0:%d/" % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(versions_json, out.strip())
# 1. GET /images with no Accept: header
# Verify version choices returned.
cmd = "curl http://0.0.0.0:%d/images" % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(versions_json, out.strip())
# 2. GET /v1/images with no Accept: header
# Verify empty images list returned.
cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(images_json, out.strip())
# 3. GET / with Accept: unknown header
# Verify version choices returned. Verify message in API log about
# unknown accept header.
cmd = "curl -H 'Accept: unknown' http://0.0.0.0:%d/" % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(versions_json, out.strip())
self.assertTrue('Unknown accept header'
in open(self.api_server.log_file).read())
# 5. GET / with an Accept: application/vnd.openstack.images-v1
# Verify empty image list returned
cmd = ("curl -H 'Accept: application/vnd.openstack.images-v1' "
"http://0.0.0.0:%d/images") % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(images_json, out.strip())
# 5. GET /images with a Accept: application/vnd.openstack.compute-v1
# header. Verify version choices returned. Verify message in API log
# about unknown accept header.
cmd = ("curl -H 'Accept: application/vnd.openstack.compute-v1' "
"http://0.0.0.0:%d/images") % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(versions_json, out.strip())
self.assertTrue('Unknown accept header'
in open(self.api_server.log_file).read())
# 6. GET /v1.0/images with no Accept: header
# Verify empty image list returned
cmd = "curl http://0.0.0.0:%d/v1.0/images" % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(images_json, out.strip())
# 7. GET /v1.a/images with no Accept: header
# Verify empty image list returned
cmd = "curl http://0.0.0.0:%d/v1.a/images" % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(images_json, out.strip())
# 8. GET /va.1/images with no Accept: header
# Verify version choices returned
cmd = "curl http://0.0.0.0:%d/va.1/images" % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertEqual(versions_json, out.strip())
def test_size_greater_2G_mysql(self): def test_size_greater_2G_mysql(self):
""" """
A test against the actual datastore backend for the registry A test against the actual datastore backend for the registry
@@ -519,7 +652,7 @@ class TestCurlApi(functional.FunctionalTest):
"-H 'X-Image-Meta-Size: %d' " "-H 'X-Image-Meta-Size: %d' "
"-H 'X-Image-Meta-Name: Image1' " "-H 'X-Image-Meta-Name: Image1' "
"-H 'X-Image-Meta-Is-Public: True' " "-H 'X-Image-Meta-Is-Public: True' "
"http://0.0.0.0:%d/v1.0/images") % (FIVE_GB, api_port) "http://0.0.0.0:%d/v1/images") % (FIVE_GB, api_port)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
@@ -539,8 +672,8 @@ class TestCurlApi(functional.FunctionalTest):
self.assertTrue(new_image_uri is not None, self.assertTrue(new_image_uri is not None,
"Could not find a new image URI!") "Could not find a new image URI!")
self.assertTrue("v1.0/images" in new_image_uri, self.assertTrue("v1/images" in new_image_uri,
"v1.0/images not in %s" % new_image_uri) "v1/images not in %s" % new_image_uri)
# 2. HEAD /images # 2. HEAD /images
# Verify image size is what was passed in, and not truncated # Verify image size is what was passed in, and not truncated
@@ -582,7 +715,7 @@ class TestCurlApi(functional.FunctionalTest):
test_data_file.write("XXX") test_data_file.write("XXX")
test_data_file.flush() test_data_file.flush()
cmd = ("curl -i -X POST --upload-file %s " cmd = ("curl -i -X POST --upload-file %s "
"http://0.0.0.0:%d/v1.0/images") % (test_data_file.name, "http://0.0.0.0:%d/v1/images") % (test_data_file.name,
api_port) api_port)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)

View File

@@ -46,7 +46,7 @@ class TestMiscellaneous(functional.FunctionalTest):
api_port = self.api_port api_port = self.api_port
registry_port = self.registry_port registry_port = self.registry_port
cmd = "curl -g http://0.0.0.0:%d/v1.0/images" % api_port cmd = "curl -g http://0.0.0.0:%d/v1/images" % api_port
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
@@ -56,7 +56,7 @@ class TestMiscellaneous(functional.FunctionalTest):
cmd = "curl -X POST -H 'Content-Type: application/octet-stream' "\ cmd = "curl -X POST -H 'Content-Type: application/octet-stream' "\
"-H 'X-Image-Meta-Name: ImageName' "\ "-H 'X-Image-Meta-Name: ImageName' "\
"-H 'X-Image-Meta-Disk-Format: Invalid' "\ "-H 'X-Image-Meta-Disk-Format: Invalid' "\
"http://0.0.0.0:%d/v1.0/images" % api_port "http://0.0.0.0:%d/v1/images" % api_port
ignored, out, err = execute(cmd) ignored, out, err = execute(cmd)
self.assertTrue('Invalid disk format' in out, self.assertTrue('Invalid disk format' in out,

View File

@@ -29,7 +29,7 @@ import webob
from glance.common import exception from glance.common import exception
from glance.registry import server as rserver from glance.registry import server as rserver
from glance.api import v1_0 as server from glance.api import v1 as server
import glance.store import glance.store
import glance.store.filesystem import glance.store.filesystem
import glance.store.http import glance.store.http

View File

@@ -24,7 +24,7 @@ import unittest
import stubout import stubout
import webob import webob
from glance.api import v1_0 as server from glance.api import v1 as server
from glance.registry import server as rserver from glance.registry import server as rserver
from tests import stubs from tests import stubs

View File

@@ -46,5 +46,5 @@ class VersionsTest(unittest.TestCase):
"links": [ "links": [
{ {
"rel": "self", "rel": "self",
"href": "http://0.0.0.0:9292/v1.0"}]}] "href": "http://0.0.0.0:9292/v1/"}]}]
self.assertEqual(results, expected) self.assertEqual(results, expected)

View File

@@ -4,7 +4,6 @@ pep8==0.5.0
pylint==0.19 pylint==0.19
anyjson anyjson
eventlet>=0.9.12 eventlet>=0.9.12
Paste
PasteDeploy PasteDeploy
routes routes
webob webob