diff --git a/docs/ENVIRONMENT.rst b/docs/ENVIRONMENT.rst index 43a35921..883a7e0a 100644 --- a/docs/ENVIRONMENT.rst +++ b/docs/ENVIRONMENT.rst @@ -106,6 +106,7 @@ Kubernetes - **PATRONI\_KUBERNETES\_USE\_ENDPOINTS**: (optional) if set to true, Patroni will use Endpoints instead of ConfigMaps to run leader elections and keep cluster state. - **PATRONI\_KUBERNETES\_POD\_IP**: (optional) IP address of the pod Patroni is running in. This value is required when `PATRONI_KUBERNETES_USE_ENDPOINTS` is enabled and is used to populate the leader endpoint subsets when the pod's PostgreSQL is promoted. - **PATRONI\_KUBERNETES\_PORTS**: (optional) if the Service object has the name for the port, the same name must appear in the Endpoint object, otherwise service won't work. For example, if your service is defined as ``{Kind: Service, spec: {ports: [{name: postgresql, port: 5432, targetPort: 5432}]}}``, then you have to set ``PATRONI_KUBERNETES_PORTS='[{"name": "postgresql", "port": 5432}]'`` and Patroni will use it for updating subsets of the leader Endpoint. This parameter is used only if `PATRONI_KUBERNETES_USE_ENDPOINTS` is set. +- **PATRONI\_KUBERNETES\_CACERT**: (optional) Specifies the file with the CA_BUNDLE file with certificates of trusted CAs to use while verifying Kubernetes API SSL certs. If not provided, patroni will use the value provided by the ServiceAccount secret. Raft ---- diff --git a/docs/SETTINGS.rst b/docs/SETTINGS.rst index 77f41c22..8999b1c3 100644 --- a/docs/SETTINGS.rst +++ b/docs/SETTINGS.rst @@ -205,6 +205,7 @@ Kubernetes - **use\_endpoints**: (optional) if set to true, Patroni will use Endpoints instead of ConfigMaps to run leader elections and keep cluster state. - **pod\_ip**: (optional) IP address of the pod Patroni is running in. This value is required when `use_endpoints` is enabled and is used to populate the leader endpoint subsets when the pod's PostgreSQL is promoted. - **ports**: (optional) if the Service object has the name for the port, the same name must appear in the Endpoint object, otherwise service won't work. For example, if your service is defined as ``{Kind: Service, spec: {ports: [{name: postgresql, port: 5432, targetPort: 5432}]}}``, then you have to set ``kubernetes.ports: [{"name": "postgresql", "port": 5432}]`` and Patroni will use it for updating subsets of the leader Endpoint. This parameter is used only if `kubernetes.use_endpoints` is set. +- **cacert**: (optional) Specifies the file with the CA_BUNDLE file with certificates of trusted CAs to use while verifying Kubernetes API SSL certs. If not provided, patroni will use the value provided by the ServiceAccount secret. .. _raft_settings: diff --git a/patroni/dcs/kubernetes.py b/patroni/dcs/kubernetes.py index 1a659036..0bedb891 100644 --- a/patroni/dcs/kubernetes.py +++ b/patroni/dcs/kubernetes.py @@ -55,16 +55,19 @@ class K8sConfig(object): if token: self._headers['authorization'] = 'Bearer ' + token - def load_incluster_config(self): + def load_incluster_config(self, ca_certs=SERVICE_CERT_FILENAME): if SERVICE_HOST_ENV_NAME not in os.environ or SERVICE_PORT_ENV_NAME not in os.environ: raise self.ConfigException('Service host/port is not set.') if not os.environ[SERVICE_HOST_ENV_NAME] or not os.environ[SERVICE_PORT_ENV_NAME]: raise self.ConfigException('Service host/port is set but empty.') - if not os.path.isfile(SERVICE_CERT_FILENAME): + + if not os.path.isfile(ca_certs): raise self.ConfigException('Service certificate file does not exists.') - with open(SERVICE_CERT_FILENAME) as f: + with open(ca_certs) as f: if not f.read(): raise self.ConfigException('Cert file exists but empty.') + self.pool_config['ca_certs'] = ca_certs + if not os.path.isfile(SERVICE_TOKEN_FILENAME): raise self.ConfigException('Service token file does not exists.') with open(SERVICE_TOKEN_FILENAME) as f: @@ -72,7 +75,6 @@ class K8sConfig(object): if not token: raise self.ConfigException('Token file exists but empty.') self._make_headers(token=token) - self.pool_config['ca_certs'] = SERVICE_CERT_FILENAME self._server = uri('https', (os.environ[SERVICE_HOST_ENV_NAME], os.environ[SERVICE_PORT_ENV_NAME])) @staticmethod @@ -613,13 +615,14 @@ class Kubernetes(AbstractDCS): self._label_selector = ','.join('{0}={1}'.format(k, v) for k, v in self._labels.items()) self._namespace = config.get('namespace') or 'default' self._role_label = config.get('role_label', 'role') + self._ca_certs = os.environ.get('PATRONI_KUBERNETES_CACERT', config.get('cacert')) or SERVICE_CERT_FILENAME config['namespace'] = '' super(Kubernetes, self).__init__(config) self._retry = Retry(deadline=config['retry_timeout'], max_delay=1, max_tries=-1, retry_exceptions=KubernetesRetriableException) self._ttl = None try: - k8s_config.load_incluster_config() + k8s_config.load_incluster_config(ca_certs=self._ca_certs) except k8s_config.ConfigException: k8s_config.load_kube_config(context=config.get('context', 'local'))