e8c93ea5ba
New documentation sections explaning how to use the backend drivers for oslo.config, also include explanation on how to use the remote_file driver. Change-Id: I45fc2155f6fe2d8bee058dac880afba8bb9bfd53 Blueprint: oslo-config-drivers
171 lines
5.7 KiB
Python
171 lines
5.7 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.
|
|
r"""
|
|
Remote File
|
|
-----------
|
|
|
|
The **remote_file** backend driver is the first driver implemented by
|
|
oslo.config. It extends the previous limit of only accessing local files
|
|
to a new scenario where it is possible to access configuration data over
|
|
the network. The **remote_file** driver is based on the **requests** module
|
|
and is capable of accessing remote files through **HTTP** or **HTTPS**.
|
|
|
|
To definition of a remote_file configuration data source can be as minimal as::
|
|
|
|
[DEFAULT]
|
|
config_source = external_config_group
|
|
|
|
[external_config_group]
|
|
driver = remote_file
|
|
uri = http://mydomain.com/path/to/config/data.conf
|
|
|
|
Or as complete as::
|
|
|
|
[DEFAULT]
|
|
config_source = external_config_group
|
|
|
|
[external_config_group]
|
|
driver = remote_file
|
|
uri = https://mydomain.com/path/to/config/data.conf
|
|
ca_path = /path/to/server/ca.pem
|
|
client_key = /path/to/my/key.pem
|
|
client_cert = /path/to/my/cert.pem
|
|
|
|
On the following sessions, you can find more information about this driver's
|
|
classes and its options.
|
|
|
|
The Driver Class
|
|
================
|
|
|
|
.. autoclass:: URIConfigurationSourceDriver
|
|
|
|
The Configuration Source Class
|
|
==============================
|
|
|
|
.. autoclass:: URIConfigurationSource
|
|
|
|
"""
|
|
|
|
import requests
|
|
import tempfile
|
|
|
|
from oslo_config import cfg
|
|
from oslo_config import sources
|
|
|
|
|
|
class URIConfigurationSourceDriver(sources.ConfigurationSourceDriver):
|
|
"""A backend 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].
|
|
|
|
:param uri: The Uniform Resource Identifier of the configuration to be
|
|
retrieved.
|
|
|
|
:param ca_path: The path to a CA_BUNDLE file or directory with
|
|
certificates of trusted CAs.
|
|
|
|
:param client_cert: Client side certificate, as a single file path
|
|
containing either the certificate only or the
|
|
private key and the certificate.
|
|
|
|
:param 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):
|
|
try:
|
|
return self._namespace._get_value(
|
|
[(group_name, option_name)],
|
|
multi=opt.multi)
|
|
except KeyError:
|
|
return (sources._NoValue, None)
|