deckhand/deckhand/control/base.py

106 lines
3.6 KiB
Python

# Copyright 2017 AT&T Intellectual Property. All other 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.
import falcon
from oslo_log import log as logging
import yaml
from deckhand import context
LOG = logging.getLogger(__name__)
class BaseResource(object):
"""Base resource class for implementing API resources."""
# Shadowing no_authentication_methods and supplying the HTTP method as a
# value (e.g. 'GET') allows that method to run without authentication. By
# default all require authentication.
# Warning: This method of skipping authentication is applied to a HTTP
# method, which ultimately maps to a resource's on_ methods.
# If a method such as on_get were to service both a list and a single
# response, both would share the skipped authentication.
no_authentication_methods = []
def on_options(self, req, resp):
self_attrs = dir(self)
methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH']
allowed_methods = []
for m in methods:
if 'on_' + m.lower() in self_attrs:
allowed_methods.append(m)
resp.headers['Allow'] = ','.join(allowed_methods)
resp.status = falcon.HTTP_200
def from_yaml(self, req, expect_list=True, allow_empty=False):
"""Reads and converts YAML-formatted request body into a dict or list
of dicts.
:param req: Falcon Request object.
:param expect_list: Whether to expect a list or an object.
:param allow_empty: Whether the request body can be empty.
:returns: List of dicts if ``expect_list`` is True or else a dict.
"""
raw_data = req.stream.read(req.content_length or 0)
if not allow_empty and not raw_data:
error_msg = ("The request body must not be empty.")
LOG.error(error_msg)
raise falcon.HTTPBadRequest(description=error_msg)
try:
if expect_list:
data = list(yaml.safe_load_all(raw_data))
else:
data = yaml.safe_load(raw_data)
except yaml.YAMLError as e:
error_msg = ("The request body must be properly formatted YAML. "
"Details: %s." % e)
LOG.error(error_msg)
raise falcon.HTTPBadRequest(description=error_msg)
if expect_list:
bad_entries = [str(i + 1) for i, x in enumerate(data)
if not x or not isinstance(x, dict)]
if bad_entries:
error_msg = (
"Expected a list of valid objects. Invalid entries "
"found at following indexes: %s." % ','.join(bad_entries))
LOG.error(error_msg)
raise falcon.HTTPBadRequest(description=error_msg)
return data
class DeckhandRequest(falcon.Request):
context_type = context.RequestContext
@property
def project_id(self):
return self.context.tenant
@property
def user_id(self):
return self.context.user
@property
def roles(self):
return self.context.roles
def __repr__(self):
return '%s, context=%s' % (self.path, self.context)