From fb5ae2ca6326f1da8902ee0e62d904cc72ef8c43 Mon Sep 17 00:00:00 2001 From: William Brown Date: Mar 01 2019 04:31:36 +0000 Subject: [PATCH 1/2] Ticket 50197 - Container init tools Bug Description: It's important that 389 Directory Server has a functional, correct, and high quality container integration system. After years of work on the server core and lib389, this is nearly possible. Importantly, containers have certain requirements we must understand. All state must be in external-filesystem volumes. We can not assume that we have an instance installed, so must create one on launch. If one exists, we need to expose it. We don't have the ability to ask questions, so we need to use environment, or work with no input at all. We can't make assumptions about backends. Finally, we need to assume that we could be a new version of the server - we don't know about anything else. Fix Description: This adds a dscontainer wrapper tool that is intended for operation inside of containers. It handles and binds many of the existing parts of lib389 for container support. I have cleaned up past container support realising how it was done wasn't as elegant as this. The dscontainer tool is intended to be the entry point from a dockerfile, IE the CMD directive. There are still some avenues to explore. For example, we could attempt to override the storage paths for logs and db rather than relying on dockerfile system links. (this may break apparmor though). https://pagure.io/389-ds-base/issue/50197 Author: William Brown Review by: ??? --- diff --git a/src/lib389/cli/dscontainer b/src/lib389/cli/dscontainer new file mode 100755 index 0000000..3f0ed05 --- /dev/null +++ b/src/lib389/cli/dscontainer @@ -0,0 +1,183 @@ +#!/usr/bin/python3 + +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2019 William Brown +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- + +# Why does this exist, and what does it do? +########################################### +# +# This entry point exists because it's hard to make 389 really "stateless" +# in the way a container environment expects, and especially with systems +# like kubernetes with volume setup etc. +# +# This script will detect if an instance exists in the volume locations +# and if one does not (new, or ephemeral) we create a container-optimised +# instance of 389-ds. +# +# If an instance *does* exist, we will start it up, and let it run. Simple +# as that! +# + +import grp +import pwd +import atexit +import os +import signal +import sys +import subprocess + + +from lib389 import DirSrv +from lib389.cli_base import setup_script_logger +from lib389.instance.setup import SetupDs +from lib389.instance.options import General2Base, Slapd2Base +from lib389.passwd import password_generate +from lib389.paths import Paths + +# We setup the logger in verbose mode to make sure debug info +# is always available! +log = setup_script_logger("container-init", True) + +def begin_magic(): + log.info("The 389 Directory Server Container Bootstrap") + # Leave this comment here: UofA let me take this code with me provided + # I gave attribution. -- wibrown + log.info("Inspired by works of: ITS, The University of Adelaide") + + # Setup our ds_paths ... + # Notice we pre-populate the instance id, which allows the start up to work correctly + # to find the correct configuration path? + # + # We wouldn't need this *except* for testing containers that build to /opt/dirsrv + paths = Paths(serverid='localhost') + + # Make sure that /data/config, /data/ssca, /data/db and /logs exist, because + # k8s may not template them out. + # + # Big note for those at home: This means you need your dockerfile to run + # something like: + # RUN mkdir -p /data/config && \ + # mkdir -p /data/ssca && \ + # mkdir -p /data/db && \ + # mkdir -p /logs && \ + # ln -s /data/config /etc/dirsrv/slapd-localhost && \ + # ln -s /data/ssca /etc/dirsrv/ssca && \ + # ln -s /data/db /var/lib/dirsrv/slapd-localhost && \ + # ln -s /logs /var/log/dirsrv/slapd-localhost + # # Temporal volumes for each instance + # VOLUME /data + # VOLUME /logs + # + # When I said this was a container tool, I really really meant it! + # + # Q: "William, why do you symlink in these locations?" + # A: Docker lets you mount in volumes. The *simpler* we can make this for a user + # the absolute beter. This means any downstream container can simply use: + # docker run -v 389_data:/data -v 389_logs:/logs ... 389-ds:latest + # If we were to use the "normal paths", we would require FOUR volume mounts, with + # cryptic paths and complexity. Not friendly at all. + # + # Q: "William, why not change the paths in the config?" + # A: Despite the fact that ds alleges support for moving content and paths, this + # is not possible for the /etc/dirsrv content unless at COMPILE time. Additionally + # some parts of the code base make assumptions. Instead of fighting legacy, we want + # results now! So we mask our limitations with symlinks. + # + for d in ['/data/config', '/data/ssca', '/data/db', '/logs']: + if not os.path.exists(d): + os.makedirs(d, mode=0o770) + + # Do we have correct permissions to our volumes? With the power of thoughts and + # prayers, we continue blindy and ... well hope. + + # Do we have an instance? We can only tell by the /data/config/container.inf + # marker file + if not os.path.exists('/data/config/container.inf'): + # Nope? Make one ... + log.info("Initialising 389-ds-container due to empty volume ...") + rpw = password_generate() + log.info("IMPORTANT: Setting cn=Directory Manager password to \"%s\"" % rpw) + + g2b = General2Base(log) + s2b = Slapd2Base(log) + # Fill in container defaults? + + g2b.set('strict_host_checking', False) + g2b.set('selinux', False) + g2b.set('systemd', False) + + s2b.set('instance_name', 'localhost') + + # We use our user/group from the current user, begause in envs like kubernetes + # it WILL NOT be dirsrv + user_name = pwd.getpwuid(os.getuid())[0] + group_name = grp.getgrgid(os.getgid())[0] + + s2b.set('user', user_name) + s2b.set('group', group_name) + s2b.set('root_password', rpw) + s2b.set('port', 3389) + s2b.set('secure_port', 3636) + + # We could fix some paths here? + + # Setup the current userid/name into the setup + # disable certain things + # set ports. + + sds = SetupDs(verbose=True, dryrun=False, log=log, containerised=True) + + # Now collect and submit + + if not sds.create_from_args(g2b.collect(), s2b.collect()): + log.error("Failed to create instance") + sys.exit(1) + + # Create the marker to say we exist. + with open('/data/config/container.inf', 'w'): + pass + + # TODO: Should we reset cn=Directory Manager from env? + # TODO: Should we set replica id from env? + # TODO: Should we set replication agreements from env? + # TODO: Should we allow re-indexing at startup from env? + + # Yep! Run it ... + # Now unlike a normal lib389 start, we use subprocess and don't fork! + # TODO: Should we pass in a loglevel from env? + log.info("Starting 389-ds-container ...") + + global ds_proc + ds_proc = subprocess.Popen([ + "%s/ns-slapd" % paths.sbin_dir, + "-D", paths.config_dir, + # See /ldap/servers/slapd/slap.h SLAPD_DEFAULT_ERRORLOG_LEVEL + "-d", "266354688", + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + # To make sure we really do shutdown, we actually re-block on the proc + # again here to be sure it's done. + def kill_ds(): + if ds_proc is None: + pass + else: + os.kill(ds_proc.pid, signal.SIGTERM) + log.info("STOPPING: Shutting down 389-ds-container ...") + ds_proc.wait() + + atexit.register(kill_ds) + + # Now wait ... + try: + ds_proc.wait() + except KeyboardInterrupt: + pass + # THE LETTER OF THE DAY IS C AND THE NUMBER IS 10 + +if __name__ == '__main__': + begin_magic() diff --git a/src/lib389/cli/dscreate b/src/lib389/cli/dscreate index ee78141..4af852b 100755 --- a/src/lib389/cli/dscreate +++ b/src/lib389/cli/dscreate @@ -28,16 +28,12 @@ fromfile_parser = subparsers.add_parser('from-file', help="Create an instance of fromfile_parser.add_argument('file', help="Inf file to use with prepared answers. You can generate an example of this with 'dscreate create-template'") fromfile_parser.add_argument('-n', '--dryrun', help="Validate system and configurations only. Do not alter the system.", action='store_true', default=False) -fromfile_parser.add_argument('-c', '--containerized', help="Indicate to the installer that this is running in a container. Used to disable systemd native components, even if they are installed.", - action='store_true', default=False) fromfile_parser.set_defaults(func=cli_instance.instance_create) interactive_parser = subparsers.add_parser('interactive', help="Start interactive installer for Directory Server installation") interactive_parser.set_defaults(func=cli_instance.instance_create_interactive) template_parser = subparsers.add_parser('create-template', help="Display an example inf answer file, or provide a file name to write it to disk.") -template_parser.add_argument('-c', '--containerized', help="Indicate to the installer that this is running in a container. Used to disable systemd native components, even if they are installed.", - action='store_true', default=False) template_parser.add_argument('template_file', nargs="?", default=None, help="Write example template to this file") template_parser.set_defaults(func=cli_instance.instance_example) diff --git a/src/lib389/lib389/cli_ctl/instance.py b/src/lib389/lib389/cli_ctl/instance.py index 5f11ecc..735de0f 100644 --- a/src/lib389/lib389/cli_ctl/instance.py +++ b/src/lib389/lib389/cli_ctl/instance.py @@ -63,10 +63,7 @@ def instance_create_interactive(inst, log, args): def instance_create(inst, log, args): - if args.containerized: - log.debug("Containerized features requested.") - - sd = SetupDs(args.verbose, args.dryrun, log, args.containerized) + sd = SetupDs(args.verbose, args.dryrun, log) if sd.create_from_inf(args.file): # print("Successfully created instance") return True @@ -76,9 +73,6 @@ def instance_create(inst, log, args): def instance_example(inst, log, args): - if args.containerized: - log.debug("Containerized features requested.") - header = """ ; ; This is a version 2 ds setup inf file. @@ -97,7 +91,7 @@ def instance_example(inst, log, args): """ g2b = General2Base(log) - s2b = Slapd2Base(log, args.containerized) + s2b = Slapd2Base(log) b2b = Backend2Base(log, "backend-userroot") if args.template_file: diff --git a/src/lib389/lib389/instance/options.py b/src/lib389/lib389/instance/options.py index fa09c24..f74e979 100644 --- a/src/lib389/lib389/instance/options.py +++ b/src/lib389/lib389/instance/options.py @@ -116,7 +116,7 @@ class General2Base(Options2): self._options['strict_host_checking'] = True self._type['strict_host_checking'] = bool - self._helptext['strict_host_checking'] = "Sets whether the server verifies the forward and reverse record set in the \"full_machine_name\" parameter. When installing this instance with GSSAPI authentication behind a load balancer, set this parameter to \"false\"." + self._helptext['strict_host_checking'] = "Sets whether the server verifies the forward and reverse record set in the \"full_machine_name\" parameter. When installing this instance with GSSAPI authentication behind a load balancer, set this parameter to \"false\". Container installs imply \"false\"." self._options['selinux'] = True self._type['selinux'] = bool @@ -137,7 +137,7 @@ class General2Base(Options2): class Slapd2Base(Options2): - def __init__(self, log, container=False): + def __init__(self, log): super(Slapd2Base, self).__init__(log) self._section = 'slapd' @@ -169,17 +169,11 @@ class Slapd2Base(Options2): self._type['prefix'] = str self._helptext['prefix'] = "Sets the file system prefix for all other directories. You can refer to this value in other fields using the {prefix} variable or the $PREFIX environment variable. Only set this parameter in a development environment." - if container: - self._options['port'] = 3389 - else: - self._options['port'] = 389 + self._options['port'] = 389 self._type['port'] = int self._helptext['port'] = "Sets the TCP port the instance uses for LDAP connections." - if container: - self._options['secure_port'] = 3636 - else: - self._options['secure_port'] = 636 + self._options['secure_port'] = 636 self._type['secure_port'] = int self._helptext['secure_port'] = "Sets the TCP port the instance uses for TLS-secured LDAP connections (LDAPS)." diff --git a/src/lib389/lib389/instance/setup.py b/src/lib389/lib389/instance/setup.py index b2e19e3..83a1a58 100644 --- a/src/lib389/lib389/instance/setup.py +++ b/src/lib389/lib389/instance/setup.py @@ -649,6 +649,7 @@ class SetupDs(object): self.log.debug("FINISH: Completed installation for %s", slapd['instance_name']) if not self.verbose: self.log.info("Completed installation for %s", slapd['instance_name']) + return True def _install_ds(self, general, slapd, backends): """ @@ -761,7 +762,7 @@ class SetupDs(object): os.chmod(dstfile, 0o440) # If we are on the correct platform settings, systemd - if general['systemd'] and not self.containerised: + if general['systemd']: # Should create the symlink we need, but without starting it. subprocess.check_call(["systemctl", "enable", @@ -829,12 +830,12 @@ class SetupDs(object): csr = tlsdb.create_rsa_key_and_csr() (ca, crt) = ssca.rsa_ca_sign_csr(csr) tlsdb.import_rsa_crt(ca, crt) - if not self.containerised and general['selinux']: + if general['selinux']: # Set selinux port label selinux_label_port(slapd['secure_port']) # Do selinux fixups - if not self.containerised and general['selinux']: + if general['selinux']: selinux_paths = ('backup_dir', 'cert_dir', 'config_dir', 'db_dir', 'ldif_dir', 'lock_dir', 'log_dir', 'run_dir', 'schema_dir', 'tmp_dir') for path in selinux_paths: @@ -861,10 +862,7 @@ class SetupDs(object): # tests with standalone.enable_tls if we do not. It's only when security; on # that we actually start listening on it. if not slapd['secure_port']: - if self.containerised: - slapd['secure_port'] = "3636" - else: - slapd['secure_port'] = "636" + slapd['secure_port'] = "636" ds_instance.config.set('nsslapd-secureport', '%s' % slapd['secure_port']) if slapd['self_sign_cert']: ds_instance.config.set('nsslapd-security', 'on') @@ -917,12 +915,13 @@ class SetupDs(object): else: self.log.debug("Skipping default SASL maps - no backend found!") + # Change the root password finally + ds_instance.config.set('nsslapd-rootpw', slapd['root_password']) + # Complete. if self.containerised: # In a container build we need to stop DirSrv at the end ds_instance.stop() else: - # If we are not a container, change the root password finally - ds_instance.config.set('nsslapd-rootpw', slapd['root_password']) # Restart for changes to take effect - this could be removed later ds_instance.restart(post_open=False) diff --git a/src/lib389/lib389/passwd.py b/src/lib389/lib389/passwd.py index eda1ea9..d0bd1d7 100644 --- a/src/lib389/lib389/passwd.py +++ b/src/lib389/lib389/passwd.py @@ -44,7 +44,10 @@ def password_hash(pw, scheme=None, bin_dir='/bin'): def password_generate(length=64): """Generate a complex password with at least one upper case letter, a lower case letter, a digit - and a special character + and a special character. The special characters are limited + to a set that can be highlighted with double-click to allow + easier copy-paste to a password-manager. Most password strength + comes from length anyway, so this is why we use a long length (64) :param length: a password length :type length: int @@ -56,13 +59,15 @@ def password_generate(length=64): # The number of possible values for a byte is 256 which is a multiple of 64 # Maybe it is an overkill for our case but it can come handy one day # (especially consider the fact we can use it for CLI tools) - chars = string.ascii_letters + string.digits + '*&' + chars = string.ascii_letters + string.digits + '-.' # Get the minimal requirements + # Don't use characters that prevent easy highlight for copy paste ... + # It's the little details that make us great pw = [random.choice(string.ascii_lowercase), random.choice(string.ascii_uppercase), random.choice(string.digits), - '!'] + '.'] # Use the simple algorithm to generate more or less secure password for i in range(length - 3): diff --git a/src/lib389/setup.py b/src/lib389/setup.py index bef89eb..8e8eed8 100644 --- a/src/lib389/setup.py +++ b/src/lib389/setup.py @@ -45,8 +45,6 @@ setup( 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', @@ -65,6 +63,7 @@ setup( 'cli/dsconf', 'cli/dscreate', 'cli/dsidm', + 'cli/dscontainer', ]), ('/usr/share/man/man8', [ 'man/dsctl.8', From 0f918de1b93acd36a52f09671e13fec2b0b4d050 Mon Sep 17 00:00:00 2001 From: William Brown Date: Mar 01 2019 04:43:59 +0000 Subject: [PATCH 2/2] Ticket 50197 - Container integration part 2 Bug Description: Rather than hardcoding behaviours into the setup process of the installer, the container init process adapts the slapd config to match what a container needs. Fix Description: To achieve this, we expose a "start" option in the from-file install which allows the post install start to be true/false. We also correct the container's locations to install ds into known paths. Finally a flag is added to dsctl to prevent certain actions from running inside a container limiting us only to maintenance actions (and still only offline) https://pagure.io/389-ds-base/issue/50197 Author: William Brown Review by: mreynolds, mhonek --- diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in index ff0160e..934e945 100644 --- a/rpm/389-ds-base.spec.in +++ b/rpm/389-ds-base.spec.in @@ -812,6 +812,7 @@ exit 0 %{_mandir}/man8/dsctl.8.gz %{_sbindir}/dsidm %{_mandir}/man8/dsidm.8.gz +%{_sbindir}/dscontainer %files -n cockpit-389-ds -f cockpit.list %{_datarootdir}/metainfo/389-console/org.cockpit-project.389-console.metainfo.xml diff --git a/src/lib389/cli/dscontainer b/src/lib389/cli/dscontainer index 3f0ed05..81195c9 100755 --- a/src/lib389/cli/dscontainer +++ b/src/lib389/cli/dscontainer @@ -30,6 +30,8 @@ import os import signal import sys import subprocess +import argparse, argcomplete +from argparse import RawTextHelpFormatter from lib389 import DirSrv @@ -56,30 +58,26 @@ def begin_magic(): # We wouldn't need this *except* for testing containers that build to /opt/dirsrv paths = Paths(serverid='localhost') - # Make sure that /data/config, /data/ssca, /data/db and /logs exist, because + # Make sure that /data/config, /data/ssca and /data/config exist, because # k8s may not template them out. # # Big note for those at home: This means you need your dockerfile to run # something like: + # EXPOSE 3389 3636 # RUN mkdir -p /data/config && \ # mkdir -p /data/ssca && \ - # mkdir -p /data/db && \ - # mkdir -p /logs && \ # ln -s /data/config /etc/dirsrv/slapd-localhost && \ # ln -s /data/ssca /etc/dirsrv/ssca && \ - # ln -s /data/db /var/lib/dirsrv/slapd-localhost && \ - # ln -s /logs /var/log/dirsrv/slapd-localhost # # Temporal volumes for each instance # VOLUME /data - # VOLUME /logs # # When I said this was a container tool, I really really meant it! # # Q: "William, why do you symlink in these locations?" # A: Docker lets you mount in volumes. The *simpler* we can make this for a user # the absolute beter. This means any downstream container can simply use: - # docker run -v 389_data:/data -v 389_logs:/logs ... 389-ds:latest - # If we were to use the "normal paths", we would require FOUR volume mounts, with + # docker run -v 389_data:/data ... 389-ds:latest + # If we were to use the "normal paths", we would require MORE volume mounts, with # cryptic paths and complexity. Not friendly at all. # # Q: "William, why not change the paths in the config?" @@ -88,7 +86,16 @@ def begin_magic(): # some parts of the code base make assumptions. Instead of fighting legacy, we want # results now! So we mask our limitations with symlinks. # - for d in ['/data/config', '/data/ssca', '/data/db', '/logs']: + for d in [ + '/data/config', + '/data/ssca', + '/data/db', + '/data/bak', + '/data/ldif', + '/data/run', + '/data/run/lock', + '/data/logs' + ]: if not os.path.exists(d): os.makedirs(d, mode=0o770) @@ -101,7 +108,6 @@ def begin_magic(): # Nope? Make one ... log.info("Initialising 389-ds-container due to empty volume ...") rpw = password_generate() - log.info("IMPORTANT: Setting cn=Directory Manager password to \"%s\"" % rpw) g2b = General2Base(log) s2b = Slapd2Base(log) @@ -110,6 +116,7 @@ def begin_magic(): g2b.set('strict_host_checking', False) g2b.set('selinux', False) g2b.set('systemd', False) + g2b.set('start', False) s2b.set('instance_name', 'localhost') @@ -124,24 +131,39 @@ def begin_magic(): s2b.set('port', 3389) s2b.set('secure_port', 3636) - # We could fix some paths here? - - # Setup the current userid/name into the setup - # disable certain things - # set ports. - + s2b.set('local_state_dir', '/data') + s2b.set('inst_dir', '/data') + s2b.set('db_dir', '/data/db') + # Why is this bak? Some dsctl commands use INST_DIR/bak, not "backup_dir" + # due to some legacy handling of paths in lib389's population of instances. + s2b.set('backup_dir', '/data/bak') + s2b.set('ldif_dir', '/data/ldif') + s2b.set('run_dir', '/data/run') + s2b.set('lock_dir', '/data/run/lock') + s2b.set('ldapi', '/data/run/slapd.socket') + + s2b.set('log_dir', '/data/logs') + s2b.set('access_log', '/data/logs/access') + s2b.set('error_log', '/data/logs/error') + s2b.set('audit_log', '/data/logs/audit') + + # Now collect and submit for creation. sds = SetupDs(verbose=True, dryrun=False, log=log, containerised=True) - # Now collect and submit - if not sds.create_from_args(g2b.collect(), s2b.collect()): log.error("Failed to create instance") sys.exit(1) - # Create the marker to say we exist. + log.info("IMPORTANT: Set cn=Directory Manager password to \"%s\"" % rpw) + + # Create the marker to say we exist. This is also a good writable permissions + # test for the volume. with open('/data/config/container.inf', 'w'): pass + # TODO: All of this is contingent on the server starting *and* + # ldapi working ... Perhaps these are better inside ns-slapd core + # and we just proxy/filter the env through? # TODO: Should we reset cn=Directory Manager from env? # TODO: Should we set replica id from env? # TODO: Should we set replication agreements from env? @@ -166,7 +188,11 @@ def begin_magic(): if ds_proc is None: pass else: - os.kill(ds_proc.pid, signal.SIGTERM) + try: + os.kill(ds_proc.pid, signal.SIGTERM) + except ProcessLookupError: + # It's already gone ... + pass log.info("STOPPING: Shutting down 389-ds-container ...") ds_proc.wait() @@ -180,4 +206,39 @@ def begin_magic(): # THE LETTER OF THE DAY IS C AND THE NUMBER IS 10 if __name__ == '__main__': - begin_magic() + parser = argparse.ArgumentParser(allow_abbrev=True, description=""" +dscontainer - this is a container entry point that will run a stateless +instance of 389-ds. You should not use this unless you are developing or +building a container image of some nature. As a result, this tool is +*extremely* opinionated, and you will need your container build file to +have certain settings to work correctly. + +\tEXPOSE 3389 3636 +\tRUN mkdir -p /data/config && \\ +\t mkdir -p /data/ssca && \\ +\t ln -s /data/config /etc/dirsrv/slapd-localhost && \\ +\t ln -s /data/ssca /etc/dirsrv/ssca && \\ +\tVOLUME /data + +This is an example of the minimal required configuration. The 389 +instance will be created with ports 3389 and 3636. *All* of the data will +be installed under /data. This means that to "reset" an instance you only +need to remove the content of /data. In the case there is no instance +one will be created. + +No backends or suffixes are created by default, as we can not assume your +domain component. The cn=Directory Manager password is randomised on +install, and can be viewed in the setup log, or can be accessed via ldapi +- the ldapi socket is placed into /data so you can access it from the +container host. + """, formatter_class=RawTextHelpFormatter) + parser.add_argument('-r', '--runit', + help="Actually run the instance! You understand what that means ...", + action='store_true', default=False, dest='runit') + argcomplete.autocomplete(parser) + + args = parser.parse_args() + + if args.runit: + begin_magic() + diff --git a/src/lib389/cli/dsctl b/src/lib389/cli/dsctl index a6df241..8061b04 100755 --- a/src/lib389/cli/dsctl +++ b/src/lib389/cli/dsctl @@ -14,6 +14,7 @@ import argparse, argcomplete import logging import sys import signal +import os from lib389.utils import get_instance_list from lib389.cli_base import _get_arg from lib389 import DirSrv @@ -44,7 +45,10 @@ parser.add_argument('--remove-all', nargs="?", default=False, const=None, ) subparsers = parser.add_subparsers(help="action") -cli_instance.create_parser(subparsers) +# We can only use the instance tools like start/stop etc in a non-container +# environment. If we are in a container, we only allow the tasks. +if not os.path.exists('/data/config/container.inf'): + cli_instance.create_parser(subparsers) cli_dbtasks.create_parser(subparsers) argcomplete.autocomplete(parser) diff --git a/src/lib389/lib389/instance/options.py b/src/lib389/lib389/instance/options.py index f74e979..d2dfe85 100644 --- a/src/lib389/lib389/instance/options.py +++ b/src/lib389/lib389/instance/options.py @@ -126,6 +126,11 @@ class General2Base(Options2): self._type['systemd'] = bool self._helptext['systemd'] = "Enables systemd platform features. If set to \"True\", dscreate auto-detects whether systemd is installed. Set this only to \"False\" in a development environment." + + self._options['start'] = True + self._type['start'] = bool + self._helptext['start'] = "Starts the instance after the install completes. If false, the instance is created but started." + self._options['defaults'] = INSTALL_LATEST_CONFIG self._type['defaults'] = str self._helptext['defaults'] = "Directory Server enables administrators to use the default values for cn=config entries from a specific version. If you set this parameter to \"{LATEST}\", which is the default, the instance always uses the default values of the latest version. For example, to configure that the instance uses default values from version 1.3.5, set this parameter to \"001003005\". The format of this value is XXXYYYZZZ, where X is the major version, Y the minor version, and Z the patch level. Note that each part of the value uses 3 digits and must be filled with leading zeros if necessary.".format(LATEST=INSTALL_LATEST_CONFIG) diff --git a/src/lib389/lib389/instance/setup.py b/src/lib389/lib389/instance/setup.py index 83a1a58..1432f33 100644 --- a/src/lib389/lib389/instance/setup.py +++ b/src/lib389/lib389/instance/setup.py @@ -919,9 +919,9 @@ class SetupDs(object): ds_instance.config.set('nsslapd-rootpw', slapd['root_password']) # Complete. - if self.containerised: - # In a container build we need to stop DirSrv at the end - ds_instance.stop() - else: + if general['start']: # Restart for changes to take effect - this could be removed later ds_instance.restart(post_open=False) + else: + # Just stop the instance now. + ds_instance.stop() diff --git a/src/lib389/lib389/nss_ssl.py b/src/lib389/lib389/nss_ssl.py index 5f225c0..d01185e 100644 --- a/src/lib389/lib389/nss_ssl.py +++ b/src/lib389/lib389/nss_ssl.py @@ -120,6 +120,19 @@ class NssSsl(object): except FileExistsError: pass + # Write a README to let people know what this is + readme_file = '%s/%s' % (self._certdb, 'README.txt') + if not os.path.exists(readme_file): + with open(readme_file, 'w') as f: + f.write(""" +SSCA - Simple Self-Signed Certificate Authority + +This is part of the 389 Directory Server project's lib389 toolkit. It +creates a simple, standalone certificate authority for testing and +development purposes. It's suitable for evaluation and testing purposes +only. + """) + # In the future we may add the needed option to avoid writing the pin # files. # Write the pin.txt, and the pwdfile.txt