Scrubber to communicate with trustedauth registry
Glance scrubber needs admin context when attempting to talk to registry to list, update and delete images. However, when registry is deployed in trusted-auth mode, appropriate admin role is required to gain admin context on registry. Closes-bug: #1439666 Change-Id: I5ff68ef8f30e73642889f8e4cde7ba06628cb0e5
This commit is contained in:
parent
1225ab48e5
commit
dcbf54672c
|
@ -131,8 +131,8 @@ def get_registry_client(cxt):
|
|||
|
||||
if CONF.send_identity_headers:
|
||||
identity_headers = {
|
||||
'X-User-Id': cxt.user,
|
||||
'X-Tenant-Id': cxt.tenant,
|
||||
'X-User-Id': cxt.user or '',
|
||||
'X-Tenant-Id': cxt.tenant or '',
|
||||
'X-Roles': ','.join(cxt.roles),
|
||||
'X-Identity-Status': 'Confirmed',
|
||||
'X-Service-Catalog': jsonutils.dumps(cxt.service_catalog),
|
||||
|
|
|
@ -42,6 +42,19 @@ scrubber_opts = [
|
|||
'performing a delete.')),
|
||||
cfg.BoolOpt('delayed_delete', default=False,
|
||||
help=_('Turn on/off delayed delete.')),
|
||||
cfg.StrOpt('admin_role', default='admin',
|
||||
help=_('Role used to identify an authenticated user as '
|
||||
'administrator.')),
|
||||
cfg.BoolOpt('send_identity_headers', default=False,
|
||||
help=_("Whether to pass through headers containing user "
|
||||
"and tenant information when making requests to "
|
||||
"the registry. This allows the registry to use the "
|
||||
"context middleware without keystonemiddleware's "
|
||||
"auth_token middleware, removing calls to the keystone "
|
||||
"auth service. It is recommended that when using this "
|
||||
"option, secure communication between glance api and "
|
||||
"glance registry is ensured by means other than "
|
||||
"auth_token middleware.")),
|
||||
]
|
||||
|
||||
scrubber_cmd_opts = [
|
||||
|
@ -73,12 +86,24 @@ class ScrubDBQueue(object):
|
|||
self.metadata_encryption_key = CONF.metadata_encryption_key
|
||||
registry.configure_registry_client()
|
||||
registry.configure_registry_admin_creds()
|
||||
self.registry = registry.get_registry_client(context.RequestContext())
|
||||
admin_tenant_name = CONF.admin_tenant_name
|
||||
admin_token = self.registry.auth_token
|
||||
self.admin_context = context.RequestContext(user=CONF.admin_user,
|
||||
tenant=admin_tenant_name,
|
||||
auth_token=admin_token)
|
||||
admin_user = CONF.admin_user
|
||||
admin_tenant = CONF.admin_tenant_name
|
||||
|
||||
if CONF.send_identity_headers:
|
||||
# When registry is operating in trusted-auth mode
|
||||
roles = [CONF.admin_role]
|
||||
self.admin_context = context.RequestContext(user=admin_user,
|
||||
tenant=admin_tenant,
|
||||
auth_token=None,
|
||||
roles=roles)
|
||||
self.registry = registry.get_registry_client(self.admin_context)
|
||||
else:
|
||||
ctxt = context.RequestContext()
|
||||
self.registry = registry.get_registry_client(ctxt)
|
||||
admin_token = self.registry.auth_token
|
||||
self.admin_context = context.RequestContext(user=admin_user,
|
||||
tenant=admin_tenant,
|
||||
auth_token=admin_token)
|
||||
|
||||
def add_location(self, image_id, location):
|
||||
"""Adding image location to scrub queue.
|
||||
|
@ -215,15 +240,27 @@ class Scrubber(object):
|
|||
|
||||
registry.configure_registry_client()
|
||||
registry.configure_registry_admin_creds()
|
||||
self.registry = registry.get_registry_client(context.RequestContext())
|
||||
|
||||
# Here we create a request context with credentials to support
|
||||
# delayed delete when using multi-tenant backend storage
|
||||
admin_user = CONF.admin_user
|
||||
admin_tenant = CONF.admin_tenant_name
|
||||
auth_token = self.registry.auth_token
|
||||
self.admin_context = context.RequestContext(user=CONF.admin_user,
|
||||
tenant=admin_tenant,
|
||||
auth_token=auth_token)
|
||||
|
||||
if CONF.send_identity_headers:
|
||||
# When registry is operating in trusted-auth mode
|
||||
roles = [CONF.admin_role]
|
||||
self.admin_context = context.RequestContext(user=admin_user,
|
||||
tenant=admin_tenant,
|
||||
auth_token=None,
|
||||
roles=roles)
|
||||
self.registry = registry.get_registry_client(self.admin_context)
|
||||
else:
|
||||
ctxt = context.RequestContext()
|
||||
self.registry = registry.get_registry_client(ctxt)
|
||||
auth_token = self.registry.auth_token
|
||||
self.admin_context = context.RequestContext(user=admin_user,
|
||||
tenant=admin_tenant,
|
||||
auth_token=auth_token)
|
||||
|
||||
self.db_queue = get_scrub_queue()
|
||||
|
||||
|
|
|
@ -317,6 +317,8 @@ class ApiServer(Server):
|
|||
self.location_strategy = 'location_order'
|
||||
self.store_type_location_strategy_preference = ""
|
||||
|
||||
self.send_identity_headers = False
|
||||
|
||||
self.conf_base = """[DEFAULT]
|
||||
verbose = %(verbose)s
|
||||
debug = %(debug)s
|
||||
|
@ -336,6 +338,7 @@ delayed_delete = %(delayed_delete)s
|
|||
owner_is_tenant = %(owner_is_tenant)s
|
||||
workers = %(workers)s
|
||||
scrub_time = %(scrub_time)s
|
||||
send_identity_headers = %(send_identity_headers)s
|
||||
image_cache_dir = %(image_cache_dir)s
|
||||
image_cache_driver = %(image_cache_driver)s
|
||||
data_api = %(data_api)s
|
||||
|
@ -541,6 +544,9 @@ class ScrubberDaemon(Server):
|
|||
self.policy_file = policy_file
|
||||
self.policy_default_rule = 'default'
|
||||
|
||||
self.send_identity_headers = False
|
||||
self.admin_role = 'admin'
|
||||
|
||||
self.conf_base = """[DEFAULT]
|
||||
verbose = %(verbose)s
|
||||
debug = %(debug)s
|
||||
|
@ -555,6 +561,8 @@ metadata_encryption_key = %(metadata_encryption_key)s
|
|||
lock_path = %(lock_path)s
|
||||
sql_connection = %(sql_connection)s
|
||||
sql_idle_timeout = 3600
|
||||
send_identity_headers = %(send_identity_headers)s
|
||||
admin_role = %(admin_role)s
|
||||
[oslo_policy]
|
||||
policy_file = %(policy_file)s
|
||||
policy_default_rule = %(policy_default_rule)s
|
||||
|
|
|
@ -76,6 +76,55 @@ class TestScrubber(functional.FunctionalTest):
|
|||
|
||||
self.stop_servers()
|
||||
|
||||
def test_delayed_delete_with_trustedauth_registry(self):
|
||||
"""
|
||||
test that images don't get deleted immediately and that the scrubber
|
||||
scrubs them when registry is operating in trustedauth mode
|
||||
"""
|
||||
self.cleanup()
|
||||
self.api_server.deployment_flavor = 'noauth'
|
||||
self.registry_server.deployment_flavor = 'trusted-auth'
|
||||
self.start_servers(delayed_delete=True, daemon=True,
|
||||
metadata_encryption_key='',
|
||||
send_identity_headers=True)
|
||||
base_headers = {
|
||||
'X-Identity-Status': 'Confirmed',
|
||||
'X-Auth-Token': '932c5c84-02ac-4fe5-a9ba-620af0e2bb96',
|
||||
'X-User-Id': 'f9a41d13-0c13-47e9-bee2-ce4e8bfe958e',
|
||||
'X-Tenant-Id': 'deae8923-075d-4287-924b-840fb2644874',
|
||||
'X-Roles': 'admin',
|
||||
}
|
||||
headers = {
|
||||
'x-image-meta-name': 'test_image',
|
||||
'x-image-meta-is_public': 'true',
|
||||
'x-image-meta-disk_format': 'raw',
|
||||
'x-image-meta-container_format': 'ovf',
|
||||
'content-type': 'application/octet-stream',
|
||||
}
|
||||
headers.update(base_headers)
|
||||
path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
|
||||
http = httplib2.Http()
|
||||
response, content = http.request(path, 'POST', body='XXX',
|
||||
headers=headers)
|
||||
self.assertEqual(201, response.status)
|
||||
image = jsonutils.loads(content)['image']
|
||||
self.assertEqual('active', image['status'])
|
||||
image_id = image['id']
|
||||
|
||||
path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
|
||||
image_id)
|
||||
http = httplib2.Http()
|
||||
response, content = http.request(path, 'DELETE', headers=base_headers)
|
||||
self.assertEqual(200, response.status)
|
||||
|
||||
response, content = http.request(path, 'HEAD', headers=base_headers)
|
||||
self.assertEqual(200, response.status)
|
||||
self.assertEqual('pending_delete', response['x-image-meta-status'])
|
||||
|
||||
self.wait_for_scrub(path, headers=base_headers)
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
def test_scrubber_app(self):
|
||||
"""
|
||||
test that the glance-scrubber script runs successfully when not in
|
||||
|
@ -114,6 +163,65 @@ class TestScrubber(functional.FunctionalTest):
|
|||
|
||||
self.stop_servers()
|
||||
|
||||
def test_scrubber_app_with_trustedauth_registry(self):
|
||||
"""
|
||||
test that the glance-scrubber script runs successfully when not in
|
||||
daemon mode and with a registry that operates in trustedauth mode
|
||||
"""
|
||||
self.cleanup()
|
||||
self.api_server.deployment_flavor = 'noauth'
|
||||
self.registry_server.deployment_flavor = 'trusted-auth'
|
||||
self.start_servers(delayed_delete=True, daemon=False,
|
||||
metadata_encryption_key='',
|
||||
send_identity_headers=True)
|
||||
base_headers = {
|
||||
'X-Identity-Status': 'Confirmed',
|
||||
'X-Auth-Token': '932c5c84-02ac-4fe5-a9ba-620af0e2bb96',
|
||||
'X-User-Id': 'f9a41d13-0c13-47e9-bee2-ce4e8bfe958e',
|
||||
'X-Tenant-Id': 'deae8923-075d-4287-924b-840fb2644874',
|
||||
'X-Roles': 'admin',
|
||||
}
|
||||
headers = {
|
||||
'x-image-meta-name': 'test_image',
|
||||
'x-image-meta-is_public': 'true',
|
||||
'x-image-meta-disk_format': 'raw',
|
||||
'x-image-meta-container_format': 'ovf',
|
||||
'content-type': 'application/octet-stream',
|
||||
}
|
||||
headers.update(base_headers)
|
||||
path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
|
||||
http = httplib2.Http()
|
||||
response, content = http.request(path, 'POST', body='XXX',
|
||||
headers=headers)
|
||||
self.assertEqual(201, response.status)
|
||||
image = jsonutils.loads(content)['image']
|
||||
self.assertEqual('active', image['status'])
|
||||
image_id = image['id']
|
||||
|
||||
path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
|
||||
image_id)
|
||||
http = httplib2.Http()
|
||||
response, content = http.request(path, 'DELETE', headers=base_headers)
|
||||
self.assertEqual(200, response.status)
|
||||
|
||||
response, content = http.request(path, 'HEAD', headers=base_headers)
|
||||
self.assertEqual(200, response.status)
|
||||
self.assertEqual('pending_delete', response['x-image-meta-status'])
|
||||
|
||||
# wait for the scrub time on the image to pass
|
||||
time.sleep(self.api_server.scrub_time)
|
||||
|
||||
# scrub images and make sure they get deleted
|
||||
exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable
|
||||
cmd = ("%s --config-file %s" %
|
||||
(exe_cmd, self.scrubber_daemon.conf_file_name))
|
||||
exitcode, out, err = execute(cmd, raise_error=False)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
self.wait_for_scrub(path, headers=base_headers)
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
def test_scrubber_delete_handles_exception(self):
|
||||
"""
|
||||
Test that the scrubber handles the case where an
|
||||
|
@ -165,7 +273,7 @@ class TestScrubber(functional.FunctionalTest):
|
|||
|
||||
self.stop_servers()
|
||||
|
||||
def wait_for_scrub(self, path):
|
||||
def wait_for_scrub(self, path, headers=None):
|
||||
"""
|
||||
NOTE(jkoelker) The build servers sometimes take longer than 15 seconds
|
||||
to scrub. Give it up to 5 min, checking checking every 15 seconds.
|
||||
|
@ -177,7 +285,7 @@ class TestScrubber(functional.FunctionalTest):
|
|||
for _ in range(wait_for / check_every):
|
||||
time.sleep(check_every)
|
||||
|
||||
response, content = http.request(path, 'HEAD')
|
||||
response, content = http.request(path, 'HEAD', headers=headers)
|
||||
if (response['x-image-meta-status'] == 'deleted' and
|
||||
response['x-image-meta-deleted'] == 'True'):
|
||||
break
|
||||
|
|
|
@ -855,8 +855,8 @@ class TestRegistryV1ClientApi(base.IsolatedUnitTest):
|
|||
def test_get_registry_client_with_identity_headers(self):
|
||||
self.config(send_identity_headers=True)
|
||||
expected_identity_headers = {
|
||||
'X-User-Id': self.context.user,
|
||||
'X-Tenant-Id': self.context.tenant,
|
||||
'X-User-Id': '',
|
||||
'X-Tenant-Id': '',
|
||||
'X-Roles': ','.join(self.context.roles),
|
||||
'X-Identity-Status': 'Confirmed',
|
||||
'X-Service-Catalog': 'null',
|
||||
|
|
Loading…
Reference in New Issue