| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You 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 os |
| import OpenSSL.crypto |
| import tempfile |
| import docker |
| import requests |
| import logging |
| from requests.auth import HTTPBasicAuth |
| from .Container import Container |
| from utils import retry_check |
| from ssl_utils.SSL_cert_utils import make_server_cert |
| |
| |
| class CouchbaseServerContainer(Container): |
| def __init__(self, feature_context, name, vols, network, image_store, command=None): |
| super().__init__(feature_context, name, "couchbase-server", vols, network, image_store, command) |
| couchbase_cert, couchbase_key = make_server_cert(f"couchbase-server-{feature_context.id}", feature_context.root_ca_cert, feature_context.root_ca_key) |
| |
| self.root_ca_file = tempfile.NamedTemporaryFile(delete=False) |
| self.root_ca_file.write(OpenSSL.crypto.dump_certificate(type=OpenSSL.crypto.FILETYPE_PEM, cert=feature_context.root_ca_cert)) |
| self.root_ca_file.close() |
| os.chmod(self.root_ca_file.name, 0o666) |
| |
| self.couchbase_cert_file = tempfile.NamedTemporaryFile(delete=False) |
| self.couchbase_cert_file.write(OpenSSL.crypto.dump_certificate(type=OpenSSL.crypto.FILETYPE_PEM, cert=couchbase_cert)) |
| self.couchbase_cert_file.close() |
| os.chmod(self.couchbase_cert_file.name, 0o666) |
| |
| self.couchbase_key_file = tempfile.NamedTemporaryFile(delete=False) |
| self.couchbase_key_file.write(OpenSSL.crypto.dump_privatekey(type=OpenSSL.crypto.FILETYPE_PEM, pkey=couchbase_key)) |
| self.couchbase_key_file.close() |
| os.chmod(self.couchbase_key_file.name, 0o666) |
| |
| def get_startup_finished_log_entry(self): |
| # after startup the logs are only available in the container, only this message is shown |
| return "logs available in" |
| |
| @retry_check(max_tries=12, retry_interval=5) |
| def _run_couchbase_cli_command(self, command): |
| (code, _) = self.client.containers.get(self.name).exec_run(command) |
| if code != 0: |
| logging.error(f"Failed to run command '{command}', returned error code: {code}") |
| return False |
| return True |
| |
| def _run_couchbase_cli_commands(self, commands): |
| return all(self._run_couchbase_cli_command(command) for command in commands) |
| |
| @retry_check(max_tries=15, retry_interval=2) |
| def _load_couchbase_certs(self): |
| response = requests.post("http://localhost:8091/node/controller/loadTrustedCAs", auth=HTTPBasicAuth("Administrator", "password123")) |
| if response.status_code != 200: |
| logging.error(f"Failed to load CA certificates, with status code: {response.status_code}") |
| return False |
| |
| response = requests.post("http://localhost:8091/node/controller/reloadCertificate", auth=HTTPBasicAuth("Administrator", "password123")) |
| if response.status_code != 200: |
| logging.error(f"Failed to reload certificates, with status code: {response.status_code}") |
| return False |
| |
| return True |
| |
| def run_post_startup_commands(self): |
| if self.post_startup_commands_finished: |
| return True |
| |
| commands = [ |
| ["couchbase-cli", "cluster-init", "-c", "localhost", "--cluster-username", "Administrator", "--cluster-password", "password123", "--services", "data,index,query", |
| "--cluster-ramsize", "2048", "--cluster-index-ramsize", "256"], |
| ["couchbase-cli", "bucket-create", "-c", "localhost", "--username", "Administrator", "--password", "password123", "--bucket", "test_bucket", "--bucket-type", "couchbase", |
| "--bucket-ramsize", "1024", "--max-ttl", "36000"], |
| ["couchbase-cli", "user-manage", "-c", "localhost", "-u", "Administrator", "-p", "password123", "--set", "--rbac-username", "clientuser", "--rbac-password", "password123", |
| "--roles", "data_reader[test_bucket],data_writer[test_bucket]", "--auth-domain", "local"], |
| ["bash", "-c", 'tee /tmp/auth.json <<< \'{"state": "enable", "prefixes": [ {"path": "subject.cn", "prefix": "", "delimiter": "."}]}\''], |
| ['couchbase-cli', 'ssl-manage', '-c', 'localhost', '-u', 'Administrator', '-p', 'password123', '--set-client-auth', '/tmp/auth.json'] |
| ] |
| if not self._run_couchbase_cli_commands(commands): |
| return False |
| |
| if not self._load_couchbase_certs(): |
| return False |
| |
| self.post_startup_commands_finished = True |
| return True |
| |
| def deploy(self): |
| if not self.set_deployed(): |
| return |
| |
| mounts = [ |
| docker.types.Mount( |
| type='bind', |
| source=self.couchbase_key_file.name, |
| target='/opt/couchbase/var/lib/couchbase/inbox/pkey.key'), |
| docker.types.Mount( |
| type='bind', |
| source=self.couchbase_cert_file.name, |
| target='/opt/couchbase/var/lib/couchbase/inbox/chain.pem'), |
| docker.types.Mount( |
| type='bind', |
| source=self.root_ca_file.name, |
| target='/opt/couchbase/var/lib/couchbase/inbox/CA/root_ca.crt') |
| ] |
| |
| self.docker_container = self.client.containers.run( |
| "couchbase:enterprise-7.2.5", |
| detach=True, |
| name=self.name, |
| network=self.network.name, |
| ports={'8091/tcp': 8091, '11210/tcp': 11210}, |
| entrypoint=self.command, |
| mounts=mounts) |