1
0
Fork 0
mirror of https://github.com/yavook/kiwi-scp.git synced 2024-11-25 05:53:00 +00:00

Merge branch 'release/0.1.1'

This commit is contained in:
Jörn-Michael Miehe 2020-08-25 18:08:44 +02:00
commit 76912f629e
16 changed files with 219 additions and 144 deletions

101
README.md
View file

@ -2,22 +2,117 @@
The simple tool for managing container servers The simple tool for managing container servers
## Quick start ## Quick start
- Learn to use `docker` with `docker-compose` - Learn to use `docker` with `docker-compose`
- Install kiwi-config - Install kiwi-config
- Look at [the example instance](../example) - Look at [the example instance](./example)
- Look at the output of `kiwi --help` - Look at the output of `kiwi --help`
- Start building your own instances - Start building your own instances
## Installation ## Installation
A convenience installer is available as [install.sh](./install.sh) in this directory.
You can `curl | sh` it using the following one-liner.
```shell script ```shell script
curl --proto '=https' --tlsv1.2 -sSf 'https://raw.githubusercontent.com/ldericher/kiwi-config/master/install.sh' | sh curl --proto '=https' --tlsv1.2 -sSf 'https://raw.githubusercontent.com/ldericher/kiwi-config/master/install.sh' | sh
``` ```
That script checks for the basic dependencies of the `kiwi` command, then downloads the main script and installs it to a location of your choice. Please consider installing `kiwi` into a directory inside your $PATH. The installer downloads the `kiwi` launcher script and installs it to a location of your choice.
Please consider installing into a directory inside your `$PATH`.
Run in a root shell or use `sudo sh` instead for system-wide installation.
You should now be able to run `kiwi init --show` and see the default configuration file.
This downloads the latest version of the main kiwi-config executable and sets it up for you.
### Adjusting environment for `kiwi`
`kiwi-config` depends on Python 3.6 (or later), [pipenv](https://pipenv.pypa.io/), and
[less](http://www.greenwoodsoftware.com/less/) being in your `$PATH`.
In some cases, notably when using a multi-version system such as
[CentOS SCL](https://wiki.centos.org/AdditionalResources/Repositories/SCL), not all of these are in your `$PATH`
at login time.
In those cases, you can simply create a `.kiwienv` file in your home directory.
It will be sourced every time you use the `kiwi` command.
For the aforementioned case where you installed `centos-release-scl` and `rh-python36`, your `~/.kiwienv` should
contain:
```shell script
#!/bin/sh
. /opt/rh/rh-python36/enable
```
## Get started ## Get started
TODO ### Create a kiwi-config instance
Any directory is implicitly a valid `kiwi-config` instance using the default configuration.
To prevent surprises however, you should run `kiwi init` in an empty directory and follow its directions before
actually using `kiwi` more.
### Concept
A `kiwi-config` instance is a directory containing a bunch of static configuration files.
"Static" there as in "those don't change during normal service operation".
These files could be anything from actual `.conf` files to entire html-web-roots.
Non-static, but persistent files are to be kept in a "service data directory", by default `/var/kiwi`.
In your `docker-compose.yml` files, you can refer to that directory as **${TARGETROOT}**.
Start the current directory as a `kiwi-config` instance using `kiwi up`, or stop it using `kiwi down`.
This also creates kiwi's internal hub network, which you can use as **kiwi_hub** in your `docker-compose.yml` files.
### Projects
A `kiwi-config` instance usually contains several projects.
A project is a collection of dependent or at least logically connected services, described by a `docker-compose.yml`.
A well-known example would be webserver + php + database.
To create a project, use the `kiwi new <project-name>` command.
By default, this creates a new disabled project.
Before enabling or starting, consider editing the new project's `docker-compose.yml` file to your liking.
Finally, enable it with `kiwi enable <project-name>`.
You can also create, enable or (analogously) disable multiple projects in a single command.
Each project will have its own place in the service data directory, which you can refer to as **${TARGETDIR}**.
Finally, start a project using `kiwi up <project-name>`.
### Advanced kiwi-config
`kiwi-config` extends the logical bounds of `docker-compose` to handling multiple projects.
#### The `kiwi_hub`
With kiwi-config, you get the internal kiwi_hub network for free.
It allows for network communication between services in different projects.
Be aware, services only connected to the kiwi_hub can't use a port mapping!
In most cases, you will want to use this:
```yaml
networks:
- default
- kiwi_hub
```
#### The `CONFDIR`
Sometimes, it's convenient to re-use configuration files across projects.
For this use case, create a directory named `conf` in a project.
Those will all be combined into a directory available as **${CONFDIR}** in your `docker-compose.yml` files.
#### For everything else, look at `kiwi --help`
#### Happy admin-ing!

View file

@ -2,7 +2,7 @@
# kiwi-config instance configuration # # kiwi-config instance configuration #
###################################### ######################################
version: '0.1.0' version: '0.1.1'
runtime: runtime:
storage: /tmp/kiwi storage: /tmp/kiwi

View file

@ -4,55 +4,39 @@
# CONSTANTS # # CONSTANTS #
############# #############
# dependencies to run kiwi-config # default installation directory
KIWI_DEPS="bash python3 pipenv less"
# default install directory
INSTALL_DIR_DEFAULT="/usr/local/bin" INSTALL_DIR_DEFAULT="/usr/local/bin"
##########
# CHECKS #
##########
printf "checking dependencies ... " ############
# CLI ARGS #
############
for dep in ${KIWI_DEPS}; do # installation directory
printf "%s, " "${dep}" install_dir="${1}"
# adjust default if given
INSTALL_DIR_DEFAULT="${1:-${INSTALL_DIR_DEFAULT}}"
if ! command -v "${dep}" >/dev/null 2>/dev/null; then
echo
echo "Dependency '${dep}' not found, please install!" >/dev/stderr
exit 1
fi
done
echo "OK"
######## ########
# MAIN # # MAIN #
######## ########
# prompt for installation directory # prompt for installation directory
valid="no" while [ ! -d "${install_dir}" ]; do
while [ "${valid}" = "no" ]; do
printf "Select installation directory [Default: '%s']: " "${INSTALL_DIR_DEFAULT}" printf "Select installation directory [Default: '%s']: " "${INSTALL_DIR_DEFAULT}"
read install_dir </dev/tty || install_dir="${INSTALL_DIR_DEFAULT}" read -r install_dir </dev/tty || install_dir="${INSTALL_DIR_DEFAULT}"
install_dir="${install_dir:-${INSTALL_DIR_DEFAULT}}" install_dir="${install_dir:-${INSTALL_DIR_DEFAULT}}"
# check # check if given dir exists
if [ -d "${install_dir}" ]; then if [ ! -d "${install_dir}" ]; then
valid="yes"
else
printf "Install directory doesn't exist. Try creating? [Y|n] " printf "Install directory doesn't exist. Try creating? [Y|n] "
read yesno </dev/tty || yesno="yes" read -r yesno </dev/tty || yesno="yes"
if [ ! "${yesno}" = "N" ] || [ ! "${yesno}" = "n" ]; then yesno=$(printf '%.1s' "${yesno}")
# check creation failure if [ ! "${yesno}" = "N" ] && [ ! "${yesno}" = "n" ]; then
if mkdir -p "${install_dir}"; then # fail this script if we can't create the install dir
valid="yes" if ! mkdir -p "${install_dir}"; then
else
echo "Invalid install directory." >/dev/stderr echo "Invalid install directory." >/dev/stderr
exit 1 exit 1
fi fi
@ -60,6 +44,11 @@ while [ "${valid}" = "no" ]; do
fi fi
done done
if [ ! -d "${install_dir}" ]; then
echo "wtf?"
exit 1
fi
# start actual installation # start actual installation
printf "Installing into '%s' ... " "${install_dir}" printf "Installing into '%s' ... " "${install_dir}"

44
kiwi
View file

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/sh
############# #############
# CONSTANTS # # CONSTANTS #
@ -10,7 +10,7 @@ KIWI_CONF_NAME="kiwi.yml"
KIWI_VERSION_TAG="etc/version_tag" KIWI_VERSION_TAG="etc/version_tag"
# dependencies to run kiwi-config # dependencies to run kiwi-config
KIWI_DEPS=(docker docker-compose) KIWI_DEPS="python3 pipenv less docker docker-compose"
# base install dir # base install dir
KIWI_BASEDIR="${HOME}/.local/lib/kiwi-config" KIWI_BASEDIR="${HOME}/.local/lib/kiwi-config"
# per-user env setup script # per-user env setup script
@ -21,28 +21,30 @@ KIWI_REPO="https://github.com/ldericher/kiwi-config"
# use latest version by default # use latest version by default
KIWI_VERSION="master" KIWI_VERSION="master"
################### ###################
# DYNAMIC STRINGS # # DYNAMIC STRINGS #
################### ###################
# directory of correct installation # directory of correct installation
function kiwi_installdir() { kiwi_installdir() {
echo "${KIWI_BASEDIR}/${KIWI_VERSION}" echo "${KIWI_BASEDIR}/${KIWI_VERSION}"
} }
# src directory in installed version # src directory in installed version
function kiwi_root() { kiwi_root() {
echo "$(kiwi_installdir)/src" echo "$(kiwi_installdir)/src"
} }
# main script in installed version # main script in installed version
function kiwi_executable() { kiwi_executable() {
echo "$(kiwi_root)/kiwi-config.py" echo "$(kiwi_root)/kiwi-config.py"
} }
# cache current work directory # cache current work directory
WORKDIR="$(pwd)" WORKDIR="$(pwd)"
################## ##################
# PER-USER SETUP # # PER-USER SETUP #
################## ##################
@ -50,20 +52,22 @@ WORKDIR="$(pwd)"
# add in environment setup # add in environment setup
if [ -f "${KIWI_ENVFILE}" ]; then if [ -f "${KIWI_ENVFILE}" ]; then
# shellcheck source=$HOME/.kiwienv # shellcheck source=$HOME/.kiwienv
source "${KIWI_ENVFILE}" . "${KIWI_ENVFILE}"
fi fi
########## ##########
# CHECKS # # CHECKS #
########## ##########
for dep in "${KIWI_DEPS[@]}"; do for dep in ${KIWI_DEPS}; do
if ! command -v "${dep}" &>/dev/null; then if ! command -v "${dep}" >/dev/null 2>/dev/null; then
echo "Dependency '${dep}' not found, please install!" >/dev/stderr echo "Dependency '${dep}' not found, please install!" >/dev/stderr
exit 1 exit 1
fi fi
done done
######## ########
# MAIN # # MAIN #
######## ########
@ -77,7 +81,7 @@ fi
# install if kiwi-config not found # install if kiwi-config not found
if [ ! -x "$(kiwi_executable)" ]; then if [ ! -x "$(kiwi_executable)" ]; then
echo -n "Installing kiwi-config v${KIWI_VERSION} into ${KIWI_BASEDIR} ... " printf "Installing kiwi-config v%s into %s ... " "${KIWI_VERSION}" "${KIWI_BASEDIR}"
# switch to temp dir # switch to temp dir
tmpdir=$(mktemp -d) tmpdir=$(mktemp -d)
@ -92,7 +96,7 @@ if [ ! -x "$(kiwi_executable)" ]; then
if [ -x "$(kiwi_executable)" ]; then if [ -x "$(kiwi_executable)" ]; then
# after version update: no need to install # after version update: no need to install
echo "kiwi-config v${KIWI_VERSION} found!" echo "v${KIWI_VERSION} already installed!"
else else
# install archive # install archive
@ -106,15 +110,6 @@ if [ ! -x "$(kiwi_executable)" ]; then
rm -rf "${tmpdir}" rm -rf "${tmpdir}"
fi fi
# check virtualenv
cd "$(kiwi_installdir)" || :
if ! pipenv --venv &>/dev/null; then
# install virtualenv
echo -n "Preparing virtualenv ... "
pipenv sync &>/dev/null
echo "OK"
fi
# go back to the original work directory # go back to the original work directory
cd "${WORKDIR}" || : cd "${WORKDIR}" || :
@ -123,10 +118,19 @@ KIWI_ROOT="$(kiwi_root)"
PIPENV_VERBOSITY=-1 PIPENV_VERBOSITY=-1
PIPENV_PIPFILE="$(kiwi_installdir)/Pipfile" PIPENV_PIPFILE="$(kiwi_installdir)/Pipfile"
export KIWI_ROOT
export KIWI_CONF_NAME export KIWI_CONF_NAME
export KIWI_ROOT
export PIPENV_VERBOSITY export PIPENV_VERBOSITY
export PIPENV_PIPFILE export PIPENV_PIPFILE
# check virtualenv
cd "$(kiwi_installdir)" || :
if ! pipenv --venv >/dev/null 2>/dev/null; then
# install virtualenv
printf "Preparing virtualenv ... "
pipenv sync >/dev/null 2>/dev/null
echo "OK"
fi
# run main script # run main script
exec pipenv run "$(kiwi_executable)" "${@}" exec pipenv run "$(kiwi_executable)" "${@}"

View file

@ -2,12 +2,11 @@ Commands for Operation:
up Bring up the whole instance, a project or service(s) inside a project up Bring up the whole instance, a project or service(s) inside a project
down Bring down the whole instance, a project or service(s) inside a project down Bring down the whole instance, a project or service(s) inside a project
update Update the whole instance, a project or service(s) inside a project update Update the whole instance, a project or service(s) inside a project
clean Cleanly sync all configs to target folder, then relaunch affected projects restart Restart the whole instance, a project or service(s) inside a project
purge Remove all running docker artifacts of this instance
Commands for Instance Management: Commands for Instance Management:
config Create or configure kiwi-config instance init Initialize or reconfigure kiwi-config instance
inspect Inspect projects in this instance, services inside a project or service(s) inside a project show Show projects in this instance, services inside a project or service(s) inside a project
cmd Run raw docker-compose command in a project cmd Run raw docker-compose command in a project
Commands for Project and Service Management: Commands for Project and Service Management:

View file

@ -1,4 +1,4 @@
kiwi-config is the tool for container server management. kiwi-config is the simple tool for managing container servers.
Features: Features:
- Group services into projects using their own docker-compose.yml - Group services into projects using their own docker-compose.yml

View file

@ -1 +1 @@
0.1.0 0.1.1

View file

@ -21,7 +21,7 @@ def are_you_sure(prompt, default="no"):
suffix = "[yes|NO]" suffix = "[yes|NO]"
answer = input( answer = input(
f"{_surround('MUST HAVE CAREFULING IN PROGRESS', '!')}\n" f"{_surround('MUST HAVE CAREFULING IN PROCESS', '!')}\n"
f"\n" f"\n"
f"{_emphasize(prompt)}\n" f"{_emphasize(prompt)}\n"
f"\n" f"\n"

View file

@ -84,6 +84,7 @@ class Project:
kwargs['env'].update({ kwargs['env'].update({
'COMPOSE_PROJECT_NAME': self.get_name(), 'COMPOSE_PROJECT_NAME': self.get_name(),
'KIWI_HUB_NAME': config['network:name'], 'KIWI_HUB_NAME': config['network:name'],
'TARGETROOT': config['runtime:storage'],
'CONFDIR': os.path.join(config['runtime:storage'], CONF_DIRECTORY_NAME), 'CONFDIR': os.path.join(config['runtime:storage'], CONF_DIRECTORY_NAME),
'TARGETDIR': self.target_dir_name() 'TARGETDIR': self.target_dir_name()
}) })

View file

@ -1,42 +1,39 @@
# local # local
from ._hidden import ConfCopyCommand, ConfPurgeCommand, NetUpCommand from ._hidden import ConfCopyCommand, NetUpCommand
from .build import BuildCommand from .build import BuildCommand
from .clean import CleanCommand
from .cmd import CmdCommand from .cmd import CmdCommand
from .config import ConfigCommand
from .disable import DisableCommand from .disable import DisableCommand
from .down import DownCommand from .down import DownCommand
from .enable import EnableCommand from .enable import EnableCommand
from .inspect import InspectCommand from .init import InitCommand
from .logs import LogsCommand from .logs import LogsCommand
from .new import NewCommand from .new import NewCommand
from .pull import PullCommand from .pull import PullCommand
from .purge import PurgeCommand
from .push import PushCommand from .push import PushCommand
from .restart import RestartCommand
from .shell import ShellCommand from .shell import ShellCommand
from .show import ShowCommand
from .up import UpCommand from .up import UpCommand
from .update import UpdateCommand from .update import UpdateCommand
__all__ = [ __all__ = [
'ConfCopyCommand', 'ConfCopyCommand',
'ConfPurgeCommand',
'NetUpCommand', 'NetUpCommand',
'BuildCommand', 'BuildCommand',
'CleanCommand',
'CmdCommand', 'CmdCommand',
'ConfigCommand',
'DisableCommand', 'DisableCommand',
'DownCommand', 'DownCommand',
'EnableCommand', 'EnableCommand',
'InspectCommand', 'InitCommand',
'LogsCommand', 'LogsCommand',
'NewCommand', 'NewCommand',
'PullCommand', 'PullCommand',
'PurgeCommand',
'PushCommand', 'PushCommand',
'RestartCommand',
'ShellCommand', 'ShellCommand',
'ShowCommand',
'UpCommand', 'UpCommand',
'UpdateCommand', 'UpdateCommand',
] ]

View file

@ -25,6 +25,7 @@ class ConfCopyCommand(SubCommand):
conf_dirs = [ conf_dirs = [
project.conf_dir_name() project.conf_dir_name()
for project in Projects.from_dir().filter_enabled() for project in Projects.from_dir().filter_enabled()
if project.has_configs()
] ]
if conf_dirs: if conf_dirs:
@ -33,28 +34,7 @@ class ConfCopyCommand(SubCommand):
logging.info(f"Sync directories: {conf_dirs}") logging.info(f"Sync directories: {conf_dirs}")
Rootkit('rsync').run([ Rootkit('rsync').run([
'rsync', '-r', *prefix_path_mnt(conf_dirs) 'rsync', '-rpt', '--delete', *prefix_path_mnt(conf_dirs)
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return True
class ConfPurgeCommand(SubCommand):
"""kiwi conf-purge"""
def __init__(self):
super().__init__(
'conf-purge',
action="Removing all configs for", add_parser=False,
description="Remove all config files in target directory"
)
def _run_instance(self, runner, args):
conf_target = f"{LoadedConfig.get()['runtime:storage']}/{CONF_DIRECTORY_NAME}"
logging.info(f"Purging directories: {conf_target}")
Rootkit().run([
'rm', '-rf', prefix_path_mnt(conf_target)
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return True return True

View file

@ -1,37 +0,0 @@
from ..projects import Projects
from ..subcommand import SubCommand
class CleanCommand(SubCommand):
"""kiwi clean"""
def __init__(self):
super().__init__(
'clean',
action="Cleaning all configs for",
description="Cleanly sync all configs to target folder, then relaunch affected projects"
)
def _run_instance(self, runner, args):
result = True
affected_projects = [
project.get_name()
for project in Projects.from_dir()
if project.has_configs()
]
for project_name in affected_projects:
args.projects = project_name
result &= runner.run('down')
# cleanly sync configs
result &= runner.run('conf-purge')
result &= runner.run('conf-copy')
# bring projects back up
for project_name in affected_projects:
args.projects = project_name
result &= runner.run('up')
return result

View file

@ -1,5 +1,12 @@
# system
import logging
import subprocess
# local # local
from ._hidden import _find_net
from ..subcommand import ServiceCommand from ..subcommand import ServiceCommand
from ..config import LoadedConfig
from ..executable import Executable
from ..misc import are_you_sure from ..misc import are_you_sure
@ -14,13 +21,27 @@ class DownCommand(ServiceCommand):
) )
def _run_instance(self, runner, args): def _run_instance(self, runner, args):
net_name = LoadedConfig.get()['network:name']
if are_you_sure([ if are_you_sure([
"This will bring down the entire instance.", "This will bring down the entire instance.",
"", "",
"This may not be what you intended, because:", "This may not be what you intended, because:",
" - Bringing down the instance stops ALL services in here", " - Bringing down the instance stops ALL services in here",
]): ]):
return super()._run_instance(runner, args) if super()._run_instance(runner, args):
# remove the hub network afterwards
if _find_net(net_name):
Executable('docker').run([
'network', 'rm', net_name
], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
logging.info(f"Network '{net_name}' removed")
else:
logging.info(f"Network '{net_name}' does not exist")
return True
return False return False

View file

@ -8,14 +8,14 @@ from ..subcommand import SubCommand
from ..config import DefaultConfig, LoadedConfig from ..config import DefaultConfig, LoadedConfig
class ConfigCommand(SubCommand): class InitCommand(SubCommand):
"""kiwi config""" """kiwi init"""
def __init__(self): def __init__(self):
super().__init__( super().__init__(
'config', 'init',
action=f"Configuring '{KIWI_CONF_NAME}' in", action=f"Initializing '{KIWI_CONF_NAME}' in",
description="Create or configure kiwi-config instance" description="Initialize or reconfigure kiwi-config instance"
) )
# -f switch: Initialize with default config # -f switch: Initialize with default config

View file

@ -0,0 +1,26 @@
# local
from ..subcommand import ServiceCommand
from ..misc import are_you_sure
class RestartCommand(ServiceCommand):
"""kiwi restart"""
def __init__(self):
super().__init__(
'restart', num_projects='?', num_services='*',
action="Restarting",
description="Restart the whole instance, a project or service(s) inside a project"
)
def _run_instance(self, runner, args):
if are_you_sure([
"This will restart the entire instance."
]):
return super()._run_instance(runner, args)
return False
def _run_services(self, runner, args, project, services):
project.compose_run(['restart', *services])
return True

View file

@ -24,14 +24,14 @@ def _print_list(strings):
_print_list(list(strings)) _print_list(list(strings))
class InspectCommand(ServiceCommand): class ShowCommand(ServiceCommand):
"""kiwi inspect""" """kiwi show"""
def __init__(self): def __init__(self):
super().__init__( super().__init__(
'inspect', num_projects='?', num_services='*', 'show', num_projects='?', num_services='*',
action="Inspecting", action="Showing",
description="Inspect projects in this instance, services inside a project or service(s) inside a project" description="Show projects in this instance, services inside a project or service(s) inside a project"
) )
def _run_instance(self, runner, args): def _run_instance(self, runner, args):