This repository has been archived on 2020-03-24. You can view files and clone it, but cannot push or open issues or pull requests.
quay/util/config/provider/k8sprovider.py
Matt Jibson 01fe548abd Use env vars to set k8s endpoint URL
The old DNS method is optionally enabled in k8s, but the env vars are
always there.

partial solution to 
2015-11-13 17:05:14 -05:00

112 lines
3.6 KiB
Python

import os
import logging
import json
import base64
from requests import Request, Session
from util.config.provider.baseprovider import get_yaml, CannotWriteConfigException
from util.config.provider.fileprovider import FileConfigProvider
logger = logging.getLogger(__name__)
KUBERNETES_API_HOST = os.environ.get('KUBERNETES_SERVICE_HOST', '')
port = os.environ.get('KUBERNETES_SERVICE_PORT')
if port:
KUBERNETES_API_HOST += ':' + port
SERVICE_ACCOUNT_TOKEN_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/token'
QE_NAMESPACE = os.environ.get('QE_K8S_NAMESPACE', 'quay-enterprise')
QE_CONFIG_SECRET = os.environ.get('QE_K8S_CONFIG_SECRET', 'quay-enterprise-config-secret')
class KubernetesConfigProvider(FileConfigProvider):
""" Implementation of the config provider that reads and writes configuration
data from a Kubernetes Secret. """
def __init__(self, config_volume, yaml_filename, py_filename):
super(KubernetesConfigProvider, self).__init__(config_volume, yaml_filename, py_filename)
self.yaml_filename = yaml_filename
# Load the service account token from the local store.
if not os.path.exists(SERVICE_ACCOUNT_TOKEN_PATH):
raise Exception('Cannot load Kubernetes service account token')
with open(SERVICE_ACCOUNT_TOKEN_PATH, 'r') as f:
self._service_token = f.read()
# Make sure the configuration volume exists.
if not self.volume_exists():
os.makedirs(config_volume)
@property
def provider_id(self):
return 'k8s'
def save_config(self, config_obj):
self._update_secret_file(self.yaml_filename, get_yaml(config_obj))
super(KubernetesConfigProvider, self).save_config(config_obj)
def save_volume_file(self, filename, flask_file):
filepath = super(KubernetesConfigProvider, self).save_volume_file(filename, flask_file)
try:
with open(filepath, 'r') as f:
self._update_secret_file(filename, f.read())
except IOError as ioe:
raise CannotWriteConfigException(str(ioe))
def _assert_success(self, response):
if response.status_code != 200:
logger.error('K8s API call failed with response: %s => %s', response.status_code,
response.text)
raise CannotWriteConfigException('K8s API call failed. Please report this to support')
def _update_secret_file(self, filename, value):
secret_data = {}
secret_data[filename] = base64.b64encode(value)
data = {
"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"name": QE_CONFIG_SECRET
},
"data": secret_data
}
secret_url = 'namespaces/%s/secrets/%s' % (QE_NAMESPACE, QE_CONFIG_SECRET)
secret = self._lookup_secret()
if not secret:
self._assert_success(self._execute_k8s_api('POST', secret_url, data))
return
if not 'data' in secret:
secret['data'] = {}
secret['data'][filename] = base64.b64encode(value)
self._assert_success(self._execute_k8s_api('PUT', secret_url, secret))
def _lookup_secret(self):
secret_url = 'namespaces/%s/secrets/%s' % (QE_NAMESPACE, QE_CONFIG_SECRET)
response = self._execute_k8s_api('GET', secret_url)
if response.status_code != 200:
return None
return json.loads(response.text)
def _execute_k8s_api(self, method, relative_url, data=None):
headers = {
'Authorization': 'Bearer ' + self._service_token
}
if data:
headers['Content-Type'] = 'application/json'
data = json.dumps(data) if data else None
session = Session()
url = 'https://%s/api/v1/%s' % (KUBERNETES_API_HOST, relative_url)
request = Request(method, url, data=data, headers=headers)
return session.send(request.prepare(), verify=False, timeout=2)