Doug Hellmann 5ad89d4021 add detail to driver options in config generator
Have the main _list_opts caller construct the driver option so
individual drivers do not need to repeat that.

Add choices with descriptions when emitting samples. We don't really
care about those for the runtime use, but they improve the output in
the config generator and documentation.

Use an OptGroup with the driver_option and dynamic_group_owner options
set instead of just a group name when describing the options.

Add sample_default values for some of the options in the URI driver.

Change-Id: I14c0a046e6c70a9108308db70a4efb70613d5bb3
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
2018-07-05 11:42:34 -03:00

138 lines
5.0 KiB
Python

# 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 requests
import tempfile
from oslo_config import cfg
from oslo_config import sources
class URIConfigurationSourceDriver(sources.ConfigurationSourceDriver):
"""A configuration source driver for remote files served through http[s].
Required options:
- uri: URI containing the file location.
Non-required options:
- ca_path: The path to a CA_BUNDLE file or directory with
certificates of trusted CAs.
- client_cert: Client side certificate, as a single file path
containing either the certificate only or the
private key and the certificate.
- client_key: Client side private key, in case client_cert is
specified but does not includes the private key.
"""
_uri_driver_opts = [
cfg.URIOpt(
'uri',
schemes=['http', 'https'],
required=True,
sample_default='https://example.com/my-configuration.ini',
help=('Required option with the URI of the '
'extra configuration file\'s location.'),
),
cfg.StrOpt(
'ca_path',
sample_default='/etc/ca-certificates',
help=('The path to a CA_BUNDLE file or directory '
'with certificates of trusted CAs.'),
),
cfg.StrOpt(
'client_cert',
sample_default='/etc/ca-certificates/service-client-keystore',
help=('Client side certificate, as a single file path '
'containing either the certificate only or the '
'private key and the certificate.'),
),
cfg.StrOpt(
'client_key',
help=('Client side private key, in case client_cert is '
'specified but does not includes the private key.'),
),
]
def list_options_for_discovery(self):
return self._uri_driver_opts
def open_source_from_opt_group(self, conf, group_name):
conf.register_opts(self._uri_driver_opts, group_name)
return URIConfigurationSource(
conf[group_name].uri,
conf[group_name].ca_path,
conf[group_name].client_cert,
conf[group_name].client_key)
class URIConfigurationSource(sources.ConfigurationSource):
"""A configuration source for remote files served through http[s].
:uri: The Uniform Resource Identifier of the configuration to be
retrieved.
:ca_path: The path to a CA_BUNDLE file or directory with
certificates of trusted CAs.
:client_cert: Client side certificate, as a single file path
containing either the certificate only or the
private key and the certificate.
:client_key: Client side private key, in case client_cert is
specified but does not includes the private key.
"""
def __init__(self, uri, ca_path=None, client_cert=None, client_key=None):
self._uri = uri
self._namespace = cfg._Namespace(cfg.ConfigOpts())
data = self._fetch_uri(uri, ca_path, client_cert, client_key)
with tempfile.NamedTemporaryFile() as tmpfile:
tmpfile.write(data.encode("utf-8"))
tmpfile.flush()
cfg.ConfigParser._parse_file(tmpfile.name, self._namespace)
def _fetch_uri(self, uri, ca_path, client_cert, client_key):
verify = ca_path if ca_path else True
cert = (client_cert, client_key) if client_cert and client_key else \
client_cert
with requests.get(uri, verify=verify, cert=cert) as response:
response.raise_for_status() # raises only in case of HTTPError
return response.text
def get(self, group_name, option_name, opt):
"""Return the value of the option from the group.
:param group_name: Name of the group.
:type group_name: str
:param option_name: Name of the option.
:type option_name: str
:param opt: The option definition.
:type opt: Opt
:returns: A tuple (value, location) where value is the option value
or oslo_config.sources._NoValue if the (group, option) is
not present in the source, and location is a LocationInfo.
"""
try:
return self._namespace._get_value(
[(group_name, option_name)],
multi=opt.multi)
except KeyError:
return (sources._NoValue, None)