Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 114 additions & 15 deletions irods_testing_environment/irods_setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# grown-up modules
import concurrent.futures
import itertools
import json
import logging
import os
import pathlib

# local modules
from . import context, database_setup, execute, irods_config, odbc_setup
from . import archive, context, database_setup, execute, irods_config, odbc_setup


class zone_info(object):
Expand Down Expand Up @@ -242,6 +242,14 @@

self.do_unattended_install = kwargs.get('do_unattended_install', False)

self.use_tls = kwargs.get('use_tls', False)
self.certificate_chain_file = str(pathlib.Path(context.irods_config()) / "chain.pem")
self.certificate_key_file = str(pathlib.Path(context.irods_config()) / "server.key")
self.dh_params_file = str(pathlib.Path(context.irods_config()) / "dhparams.pem")
self.ca_certificate_file = str(pathlib.Path(context.irods_config()) / "server.crt")
self.ca_certificate_path = "" # This is optional but should be defined.
self.verify_server = "cert"

return self


Expand Down Expand Up @@ -290,6 +298,22 @@
input_args.insert(4, str(self.provides_local_storage))
input_args.insert(5, str(self.resource_name))
input_args.insert(6, str(self.vault_directory))

# Insert entries for TLS prompts (added in 5.1.0).
if self.irods_version >= (5, 0, 90):
insert_index = itertools.count(7)
if self.use_tls:
# Prompt for generating self-signed certificate. Testing environment takes care of this, so decline.
input_args.insert(next(insert_index), "no")
# tls_server
input_args.insert(next(insert_index), self.certificate_chain_file)
input_args.insert(next(insert_index), self.certificate_key_file)
input_args.insert(next(insert_index), self.dh_params_file)
# tls_client
input_args.insert(next(insert_index), self.ca_certificate_file)
input_args.insert(next(insert_index), self.ca_certificate_path)
input_args.insert(next(insert_index), str(2 if self.verify_server == "cert" else 1))
input_args.insert(next(insert_index), "") # confirmation
# Handle the difference between 4.2 servers and 4.3 servers.
elif self.irods_version >= (4, 3, 0):
input_args.insert(3, str(self.provides_local_storage))
Expand Down Expand Up @@ -364,6 +388,22 @@
input_args.insert(12, str(self.provides_local_storage))
input_args.insert(13, str(self.resource_name))
input_args.insert(14, str(self.vault_directory))

# Insert entries for TLS prompts (added in 5.1.0).
if self.irods_version >= (5, 0, 90):
insert_index = itertools.count(15)
if self.use_tls:
# Prompt for generating self-signed certificate. Testing environment takes care of this, so decline.
input_args.insert(next(insert_index), "no")
# tls_server
input_args.insert(next(insert_index), self.certificate_chain_file)
input_args.insert(next(insert_index), self.certificate_key_file)
input_args.insert(next(insert_index), self.dh_params_file)
# tls_client
input_args.insert(next(insert_index), self.ca_certificate_file)
input_args.insert(next(insert_index), self.ca_certificate_path)
input_args.insert(next(insert_index), str(2 if self.verify_server == "cert" else 1))
input_args.insert(next(insert_index), "") # confirmation
# Handle the difference between 4.2 servers and 4.3 servers.
elif self.irods_version >= (4, 3, 0):
input_args.insert(11, str(self.provides_local_storage))
Expand Down Expand Up @@ -570,6 +610,29 @@
"server_control_plane_timeout_milliseconds": 10000
})

else:
if self.use_tls:

Check failure on line 614 in irods_testing_environment/irods_setup.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff PLR5501

PLR5501: Use `elif` instead of `else` then `if`, to reduce indentation [Pylint:collapsible-else-if]
json_input["server_config"]["client_server_policy"] = "CS_NEG_REQUIRE"
json_input["server_config"]["tls_server"] = {
"certificate_chain_file": self.certificate_chain_file,
"certificate_key_file": self.certificate_key_file,
"dh_params_file": self.dh_params_file,
}
json_input["server_config"]["tls_client"] = {"verify_server": self.verify_server}
if self.ca_certificate_file:
json_input["server_config"]["tls_client"]["ca_certificate_file"] = self.ca_certificate_file
json_input["service_account_environment"]["irods_ssl_ca_certificate_file"] = (
self.ca_certificate_file
)
if self.ca_certificate_path:
json_input["server_config"]["tls_client"]["ca_certificate_path"] = self.ca_certificate_path
json_input["service_account_environment"]["irods_ssl_ca_certificate_path"] = (
self.ca_certificate_path
)

json_input["service_account_environment"]["irods_client_server_policy"] = "CS_NEG_REQUIRE"
json_input["service_account_environment"]["irods_ssl_verify_server"] = self.verify_server

return json.dumps(json_input, sort_keys=True, indent=4)


Expand Down Expand Up @@ -788,6 +851,28 @@
"server_control_plane_timeout_milliseconds": 10000
})

else:
if self.use_tls:

Check failure on line 855 in irods_testing_environment/irods_setup.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff PLR5501

PLR5501: Use `elif` instead of `else` then `if`, to reduce indentation [Pylint:collapsible-else-if]
json_input["server_config"]["client_server_policy"] = "CS_NEG_REQUIRE"
json_input["server_config"]["tls_server"] = {
"certificate_chain_file": self.certificate_chain_file,
"certificate_key_file": self.certificate_key_file,
"dh_params_file": self.dh_params_file,
}
json_input["server_config"]["tls_client"] = {"verify_server": self.verify_server}
if self.ca_certificate_file:
json_input["server_config"]["tls_client"]["ca_certificate_file"] = self.ca_certificate_file
json_input["service_account_environment"]["irods_ssl_ca_certificate_file"] = (
self.ca_certificate_file
)
if self.ca_certificate_path:
json_input["server_config"]["tls_client"]["ca_certificate_path"] = self.ca_certificate_path
json_input["service_account_environment"]["irods_ssl_ca_certificate_path"] = (
self.ca_certificate_path
)
json_input["service_account_environment"]["irods_client_server_policy"] = "CS_NEG_REQUIRE"
json_input["service_account_environment"]["irods_ssl_verify_server"] = self.verify_server

return json.dumps(json_input, sort_keys=True, indent=4)

def build(self):
Expand Down Expand Up @@ -911,6 +996,24 @@
from . import container_info
from . import irods_config

if kwargs.get("use_tls", False):
config_path = pathlib.Path(context.irods_config())
key_file = config_path / 'server.key'
dhparams_file = config_path / 'dhparams.pem'
chain_file = config_path / 'chain.pem'
cert_file = config_path / 'server.crt'

# Chain file and cert file use the same source for self-signed certs.
archive.copy_files_in_container(
container,
[
(kwargs.get("path_to_key_file_on_host"), key_file),
(kwargs.get("path_to_cert_file_on_host"), chain_file),
(kwargs.get("path_to_cert_file_on_host"), cert_file),
(kwargs.get("path_to_dhparams_file_on_host"), dhparams_file),
],
)

try:
if stop_irods(container) != 0:
logging.debug(f'[{container.name}] failed to stop iRODS server before setup')
Expand Down Expand Up @@ -939,8 +1042,12 @@
run_setup_script = 'bash -c \'{} {} --json_configuration_file /input\''.format(
container_info.python(container), path_to_setup_script)
else:
run_setup_script = 'bash -c \'{} {} < /input\''.format(
container_info.python(container), path_to_setup_script)
args = []
if irods_config.get_irods_version(container) >= (5, 0, 90):
if kwargs.get("use_tls", False):

Check failure on line 1047 in irods_testing_environment/irods_setup.py

View workflow job for this annotation

GitHub Actions / ruff-lint / ruff-check

Ruff SIM102

SIM102: Use a single `if` statement instead of nested `if` statements [flake8-simplify:collapsible-if]
args.append("--tls")
args = " ".join(args)
run_setup_script = f'bash -c \'{container_info.python(container)} {path_to_setup_script} {args} < /input\''
ec = execute.execute_command(container, run_setup_script)
if ec != 0:
raise RuntimeError('failed to set up iRODS server [{}]'.format(container.name))
Expand Down Expand Up @@ -1003,9 +1110,7 @@

logging.warning('setting up iRODS catalog provider [{}]'.format(csp_container.name))

setup_irods_server(csp_container,
setup_input,
do_unattended_install=kwargs.get('do_unattended_install', False))
setup_irods_server(csp_container, setup_input, **kwargs)


def setup_irods_catalog_consumer(ctx,
Expand Down Expand Up @@ -1052,9 +1157,7 @@

logging.warning('setting up iRODS catalog consumer [{}]'.format(csc_container.name))

setup_irods_server(csc_container,
setup_input,
do_unattended_install=kwargs.get('do_unattended_install', False))
setup_irods_server(csc_container, setup_input, **kwargs)


def setup_irods_catalog_consumers(ctx,
Expand All @@ -1072,8 +1175,6 @@
consumer service name in the Compose project will be
targeted. If an empty list is provided, nothing happens.
"""
import concurrent.futures

catalog_consumer_containers = ctx.compose_project.containers(
service_names=[context.irods_catalog_consumer_service()])

Expand Down Expand Up @@ -1170,8 +1271,6 @@
zone_info_list,
odbc_driver=None,
**kwargs):
import concurrent.futures

rc = 0

with concurrent.futures.ThreadPoolExecutor() as executor:
Expand Down
28 changes: 23 additions & 5 deletions irods_testing_environment/services.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# grown-up modules
"""Utility functions for managing Docker Compose services and setting up iRODS topologies."""

import logging
import os
import pathlib

# local modules
from . import context
from . import irods_setup
from . import context, irods_setup, tls_setup
from .install import install


def create_topologies(ctx,
zone_count,
externals_directory=None,
Expand Down Expand Up @@ -51,7 +52,24 @@ def create_topologies(ctx,
# This should generate a list of identical zone infos
zone_info_list = irods_setup.get_info_for_zones(ctx, zone_names, consumer_count)

irods_setup.setup_irods_zones(ctx, zone_info_list, odbc_driver=odbc_driver, **kwargs)
if kwargs.get("use_tls", False):
# The testing environment is using a self-signed certificate, so the certificate, key, and dhparams should be
# generated ONCE and copied to each server.
key, key_file = tls_setup.generate_tls_certificate_key()
cert_file = tls_setup.generate_tls_self_signed_certificate(key)
dhparams_file = tls_setup.generate_tls_dh_params()
kwargs["path_to_key_file_on_host"] = key_file
kwargs["path_to_cert_file_on_host"] = cert_file
kwargs["path_to_dhparams_file_on_host"] = dhparams_file

try:
irods_setup.setup_irods_zones(ctx, zone_info_list, odbc_driver=odbc_driver, **kwargs)

finally:
if kwargs.get("use_tls", False):
pathlib.Path(key_file).unlink()
pathlib.Path(cert_file).unlink()
pathlib.Path(dhparams_file).unlink()


def create_topology(ctx,
Expand Down
41 changes: 24 additions & 17 deletions stand_it_up.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
# grown-up modules
import compose.cli.command
import docker
import logging
import os

# local modules
from irods_testing_environment import context
from irods_testing_environment import services
from irods_testing_environment import tls_setup
import docker

import compose.cli.command
from irods_testing_environment import context, irods_config, services, tls_setup

if __name__ == "__main__":
import argparse
Expand Down Expand Up @@ -63,14 +60,24 @@

# Bring up the services
logging.debug('bringing up project [{}]'.format(ctx.compose_project.name))
services.create_topology(ctx,
externals_directory=args.irods_externals_package_directory,
package_directory=args.package_directory,
package_version=args.package_version,
odbc_driver=args.odbc_driver,
consumer_count=args.consumer_count,
install_packages=args.install_packages,
do_unattended_install=args.do_unattended_install)

if args.use_tls:
services.create_topology(
ctx,
externals_directory=args.irods_externals_package_directory,
package_directory=args.package_directory,
package_version=args.package_version,
odbc_driver=args.odbc_driver,
consumer_count=args.consumer_count,
install_packages=args.install_packages,
do_unattended_install=args.do_unattended_install,
use_tls=args.use_tls,
)

containers = [
ctx.docker_client.containers.get(
context.container_name(ctx.compose_project.name, context.irods_catalog_provider_service())
)
]

# TLS configuration happens in setup as of 5.1.0, so only do this for prior versions when requested.
if args.use_tls and irods_config.get_irods_version(containers[0]) < (5, 0, 90):
tls_setup.configure_tls_in_zone(ctx.docker_client, ctx.compose_project)
Loading