db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek#
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# Secrets responder test client
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek#
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# Copyright (c) 2016 Red Hat, Inc.
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek#
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# This is free software; you can redistribute it and/or modify it
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# under the terms of the GNU General Public License as published by
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# the Free Software Foundation; version 2 only
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek#
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# This program is distributed in the hope that it will be useful, but
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# WITHOUT ANY WARRANTY; without even the implied warranty of
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# General Public License for more details.
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek#
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# You should have received a copy of the GNU General Public License
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek# along with this program. If not, see <http://www.gnu.org/licenses/>.
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek#
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekimport socket
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekimport requests
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekfrom requests.adapters import HTTPAdapter
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekfrom requests.packages.urllib3.connection import HTTPConnection
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekfrom requests.packages.urllib3.connectionpool import HTTPConnectionPool
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekfrom requests.compat import quote, unquote, urlparse
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekclass HTTPUnixConnection(HTTPConnection):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def __init__(self, host, timeout=60, **kwargs):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek super(HTTPUnixConnection, self).__init__('localhost')
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.unix_socket = host
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.timeout = timeout
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def connect(self):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek sock = socket.socket(family=socket.AF_UNIX)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek sock.settimeout(self.timeout)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek sock.connect(self.unix_socket)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.sock = sock
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekclass HTTPUnixConnectionPool(HTTPConnectionPool):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek scheme = 'http+unix'
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek ConnectionCls = HTTPUnixConnection
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekclass HTTPUnixAdapter(HTTPAdapter):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def get_connection(self, url, proxies=None):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek # proxies, silently ignored
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek path = unquote(urlparse(url).netloc)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return HTTPUnixConnectionPool(path)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekclass SecretsHttpClient(object):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek secrets_sock_path = '/var/run/secrets.socket'
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek secrets_container = 'secrets'
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def __init__(self, content_type='application/json', sock_path=None):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek if sock_path is None:
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek sock_path = self.secrets_sock_path
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.content_type = content_type
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.session = requests.Session()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.session.mount('http+unix://', HTTPUnixAdapter())
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.headers = dict({'Content-Type': content_type})
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.url = 'http+unix://' + \
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek quote(sock_path, safe='') + \
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek '/' + \
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self.secrets_container
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self._last_response = None
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def _join_url(self, resource):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek path = self.url.rstrip('/') + '/'
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek if resource is not None:
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek path = path + resource.lstrip('/')
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return path
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def _add_headers(self, **kwargs):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek headers = kwargs.get('headers', None)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek if headers is None:
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek headers = dict()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek headers.update(self.headers)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return headers
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def _request(self, cmd, path, **kwargs):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self._last_response = None
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek url = self._join_url(path)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek kwargs['headers'] = self._add_headers(**kwargs)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek self._last_response = cmd(url, **kwargs)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return self._last_response
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek @property
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def last_response(self):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return self._last_response
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def get(self, path, **kwargs):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return self._request(self.session.get, path, **kwargs)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def list(self, **kwargs):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return self._request(self.session.get, None, **kwargs)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def put(self, name, **kwargs):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return self._request(self.session.put, name, **kwargs)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def delete(self, name, **kwargs):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return self._request(self.session.delete, name, **kwargs)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def post(self, name, **kwargs):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return self._request(self.session.post, name, **kwargs)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekclass SecretsLocalClient(SecretsHttpClient):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def list_secrets(self):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res = self.list()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res.raise_for_status()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek simple = res.json()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return simple
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def get_secret(self, name):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res = self.get(name)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res.raise_for_status()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek simple = res.json()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek ktype = simple.get("type", None)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek if ktype != "simple":
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek raise TypeError("Invalid key type: %s" % ktype)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return simple["value"]
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def set_secret(self, name, value):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res = self.put(name, json={"type": "simple", "value": value})
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res.raise_for_status()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def del_secret(self, name):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res = self.delete(name)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res.raise_for_status()
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek def create_container(self, name):
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res = self.post(name)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek res.raise_for_status()