├── .github
└── CODEOWNERS
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.rst
├── docs
├── 404.html
├── CNAME
├── favicon.ico
├── google747986e3861ca881.html
├── images
│ ├── trivialrc.svg
│ └── trivialrc.xml
├── index.html
├── pages
│ ├── index.html
│ └── info
│ │ └── index.html
├── robots.txt
├── sitemap.xml
├── theme
│ ├── bootstrap-pygments.bundle.min.css
│ ├── bootstrap.min.css
│ ├── bootstrap.min.responsive.css
│ ├── icons
│ │ ├── glyphicons-halflings-white.png
│ │ └── glyphicons-halflings.png
│ ├── local.min.css
│ └── pygments.min.css
├── trc
└── trc.sha256
├── examples
├── README.md
├── config-files
│ ├── README.md
│ └── trc.d
│ │ ├── async.info
│ │ ├── boot.funcs
│ │ ├── boot.setvars
│ │ ├── halt.delfile
│ │ ├── sync.1
│ │ └── sync.2
├── docker-base-images
│ ├── Dockerfile.alpine
│ ├── Dockerfile.centos
│ └── README.md
├── docker-config-templates
│ ├── Dockerfile
│ ├── README.md
│ ├── haproxy.cfg.ftpl
│ └── trc.d
│ │ ├── async.haproxy
│ │ └── boot.make-conf
├── docker-service-discovery
│ ├── Dockerfile
│ ├── README.md
│ └── trc.d
│ │ ├── boot.sd-reg
│ │ ├── halt.sd-unreg
│ │ └── sync.app
├── docker-two-apps
│ ├── Dockerfile
│ ├── README.md
│ ├── app
│ │ ├── app.py
│ │ └── favicon.ico
│ └── etc
│ │ ├── nginx
│ │ └── nginx.conf
│ │ ├── trc.d
│ │ ├── async.nginx
│ │ └── async.uwsgi
│ │ └── uwsgi.ini
├── one-liners
│ └── README.md
├── process-manager
│ ├── README.md
│ ├── trc-example-procmgr.service
│ └── trc.d
│ │ ├── async.netlistener
│ │ ├── async.netlistener-logger
│ │ ├── async.netmon
│ │ ├── async.netmon-logger
│ │ ├── async.netsender
│ │ ├── boot.create-logs
│ │ └── halt.remove-logs
├── reliable-tests-for-docker-images
│ └── README.md
├── self-configuring
│ ├── README.md
│ └── trc.boot.make-self-conf
└── sync-run-of-async-groups
│ ├── README.md
│ ├── sub-trc
│ └── trc.boot.async-run-cmds
│ └── trc.boot.serial-run-groups
├── src.docs
├── .gitignore
├── Makefile
├── content
│ ├── images
│ │ ├── trivialrc.svg
│ │ └── trivialrc.xml
│ ├── pages
│ │ ├── 404.rst
│ │ └── info.rst
│ └── static
│ │ ├── favicon.ico
│ │ ├── google747986e3861ca881.html
│ │ └── robots.txt
├── pelicanconf.py
└── publishconf.py
├── tests
├── config-test.sh
├── exitcodes-test.sh
├── modes-test.sh
├── trc.d
│ ├── async.info
│ ├── boot.funcs
│ ├── boot.setvars
│ ├── halt.goodbye
│ └── sync.sleep
└── verbose-test.sh
├── trc
└── version
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * vorakl@protonmail.com
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: bash
2 |
3 | before_script:
4 | - curl -sSLf https://github.com/bmizerany/roundup/tarball/v0.0.5 | tar xvzf -
5 | - cd bmizerany-roundup-*
6 | - ./configure
7 | - make
8 | - sudo make install
9 | - cd ..
10 |
11 | script: make test
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | * 1.1.10
2 | * FIX: many README files have been fixed. Thanks to Oren Schulman.
3 | * 1.1.7
4 | * ADD: more examples
5 | * DEL: deprecated names of configuration files have been removed (`trc.fg.*`, `trc.bg.*`, `trc.sd.*`)
6 | * 1.1.6
7 | * FIX: now it ignores errors in the main exit hook
8 | * ADD: more examples
9 | * ADD: sha256 chaeck sum for the release
10 | * 1.1.5
11 | * ADD: new command line options `-v` and `--version` to print the version
12 | * ADD: new command line options `-h` and `--help` to print a short help message
13 | * ADD: new command line options `-w` and `--workdir` to set the directory with config files
14 | * 1.1.4
15 | * ADD: a new wait policy `wait_err` which waits for a first failed command
16 | * ADD: a new stage "halt" (`-H` command line option or `halt.*` files) that runs on exit from the main process
17 | * 1.1.3
18 | * ADD: a new command line option `-F` that runs commands in the foreground (to run more than one foreground processes)
19 | * 1.1.2
20 | * ADD: on exiting from sub-process it tries to kill all possible child processes if any
21 | * 1.1.1
22 | * FIX: fixed the bug in catching exit code of foregraound processes in "wait_any" mode
23 | * 1.1.0
24 | * ADD: a new stage "boot" (`-B` command line option or `boot.*` files) that runs in the main namespace and results (like environment variables) are "visible" in all other sub-processes
25 | * ADD: more ways to present configs:
26 | * ./trc.d/{boot,sync,async,halt}.*
27 | * ./trc.{boot,sync,async,halt}.*
28 | * ADD: examples
29 | * ADD: tests
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright 2016-2020 Oleksii Tsvietnov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # (c) Oleksii Tsvietnov, vorakl@protonmail.com
2 | #
3 | # Variables:
4 | ECHO_BIN ?= echo
5 | CP_BIN ?= cp
6 | SED_BIN ?= sed
7 | PWD_BIN ?= pwd
8 | BASENAME_BIN ?= basename
9 | GIT_BIN ?= git
10 | SHA256SUM_BIN ?= sha256sum
11 |
12 | # -------------------------------------------------------------------------
13 | # Set a default target
14 | .MAIN: usage
15 |
16 | DIR = $(shell ${PWD_BIN} -P)
17 | SELF = $(shell ${BASENAME_BIN} ${DIR})
18 | VER = $(shell ${SED_BIN} -n '1s/^[[:space:]]*//; 1s/[[:space:]]*$$//; 1p' ${DIR}/version)
19 | VERSION ?= ${VER}
20 | LAST_COMMIT = $(shell ${GIT_BIN} log -1 | sed -n '/^commit/s/^commit //p')
21 |
22 | usage:
23 | @${ECHO_BIN} "Usage: make [target] ..."
24 | @${ECHO_BIN} ""
25 | @${ECHO_BIN} "Examples: make setver"
26 | @${ECHO_BIN} " make VERSION=v1.15.2 setver"
27 | @${ECHO_BIN} " make settag"
28 | @${ECHO_BIN} " make push"
29 | @${ECHO_BIN} " make release"
30 | @${ECHO_BIN} " make VERSION=v1.1.14 release"
31 | @${ECHO_BIN} ""
32 | @${ECHO_BIN} "Description:"
33 | @${ECHO_BIN} " setver Set a new version (is taken from environment or file)."
34 | @${ECHO_BIN} " settag Set a new version as a tag to the last commit."
35 | @${ECHO_BIN} " push Push to the repo (with tags)."
36 | @${ECHO_BIN} " release Set a version, tag and push to the repo."
37 | @${ECHO_BIN} " test Run all tests"
38 | @${ECHO_BIN} ""
39 |
40 | test:
41 | @(cd tests && roundup)
42 |
43 | setver:
44 | @${ECHO_BIN} "Setting version to ${VERSION}"
45 | @${SED_BIN} -i "s/trc_version=\".*$$/trc_version=\"${VERSION}\"/" ${DIR}/trc
46 | @${SED_BIN} -i "1s/.*/${VERSION}/" ${DIR}/version
47 |
48 | settag:
49 | @${ECHO_BIN} "Setting ${VERSION} as a tag to ${LAST_COMMIT}"
50 | @${GIT_BIN} tag ${VERSION} ${LAST_COMMIT} 2>/dev/null || true
51 |
52 | push:
53 | @${ECHO_BIN} "Pushing commits..."
54 | @${GIT_BIN} push origin
55 | @${ECHO_BIN} "Pushing tags..."
56 | @${GIT_BIN} push origin ${VERSION}
57 |
58 | publish:
59 | @${CP_BIN} -f trc docs/
60 | @(cd docs && ${SHA256SUM_BIN} trc > trc.sha256)
61 |
62 | cirelease: test setver settag publish
63 | @${GIT_BIN} add .
64 | @${GIT_BIN} ci -m "Release new version: ${VERSION}"
65 |
66 | release: cirelease push
67 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 |
2 | The minimalistic Run-time Configuration (RC) system and process manager
3 | #######################################################################
4 |
5 | :slug: info
6 | :summary: The minimalistic Run-time Configuration (RC) system and process manager
7 |
8 | * Community_
9 | * Introduction_
10 | * Installation_
11 | * `The installation on top of CentOS Linux base image`_
12 | * `The installation on top of Alpine Linux base image`_
13 | * `How to get started?`_
14 | * `Command line options`_
15 | * `Run stages`_
16 | * `Wait policies`_
17 | * `Verbose levels`_
18 | * `Integrated functions`_
19 | * `Useful global variables`_
20 |
21 | Community
22 | =========
23 |
24 | There are a few options for the communication:
25 |
26 | * ``IRC``: #trivialrc channel on the OFTC network (irc.oftc.net)
27 | * ``Mailing list``: trivialrc-dev@freelists.org, subscribe_
28 |
29 | Introduction
30 | ============
31 |
32 | The minimalistic Run-time Configuration (RC) system and process manager is written in pure BASH and uses just a few external utilities like ``ls``, ``ps``, ``date`` and ``sleep``. Minimally, installation of TrivialRC consists of only one file which can be downloaded directly from the Github. Originaly, it was designed for use in containers but it also can be well used for running a group of processes asynchronously and synchronously, as well as managing their running order and exit codes.
33 |
34 | TrivialRC is not a replacement for an init process that usually resides in ``/sbin/init`` and has a PID 1. In containers for this purpose projects like dumb-init_ or tini_ can be used, although in most cases, having only TrivialRC as a first/main process (PID 1) in containers is quite enough. In terms of Docker, the best place for it is ENTRYPOINT.
35 |
36 | TrivialRC is an equivalent to well known ``/etc/rc`` for xBSD users. The RC system that is used for managing startup and shutdown processes. It can start and stop one or more processes, in parallel or sequentially, on back- or foreground, react differently in case of process failures, etc. All commands can be specified in the command line if they are relatively simple, or in separate files if a more comprehensive scenario is needed. That's why it can be used as a simplest tool for managing a group of process and be a lightweight replacement for solutions like Supervisor_.
37 |
38 | For instance, in docker images when TrivialRC is used as an Entrypoint, it doesn't reveal itself by default, does not affect any configuration and behaves absolutely transparently. So, you can add it into any Dockerfiles which do not have an entrypoint yet and get by this the full control under processes with fairly detailed logs of what's is going on inside a container. Please, take a look at examples__ for more information.
39 |
40 | __ https://github.com/vorakl/TrivialRC/tree/master/examples
41 |
42 |
43 | Installation
44 | ============
45 |
46 | Basically, all you need to install TrivialRC is download the latest release of the script from ``http://trivialrc.vorakl.com/trc``
47 | and give it an execute permission. By default, it looks for configuration files in the same directory from which it was invoked but this behavior can be changed by setting a work directory (``-w|--workdir`` parametr). So, if you are going to use configuration files and keep them in ``/etc/``, then you would probably want to install the script to /etc/ as well and simply run it without specifying any parametrs.
48 |
49 | Another option in this case could be to install the script in a more appropriate path but don't forget to specify ``--workdir /etc`` parametr every time when you invoke this rc system. Both options are possible and depend more on a particular use case.
50 | For instance, in case of using in a docker container, I personaly prefer to have all configuration in separate files in ``trc.d/`` sub-directory and copy it together with the script in ``/etc/``.
51 |
52 |
53 | The installation on top of CentOS Linux base image
54 | --------------------------------------------------
55 |
56 | This is an example of how it would look in a Dockerfile with `centos:latest`_ as base image:
57 |
58 | .. code-block:: bash
59 |
60 | FROM centos:latest
61 |
62 | RUN curl -sSLfo /etc/trc http://trivialrc.vorakl.com/trc && \
63 | ( cd /etc && curl -sSLf http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \
64 | chmod +x /etc/trc && \
65 | /etc/trc --version
66 |
67 | # Uncomment this if you have configuration files in trc.d/
68 | # COPY trc.d/ /etc/trc.d/
69 |
70 | ENTRYPOINT ["/etc/trc"]
71 |
72 |
73 | The installation on top of Alpine Linux base image
74 | --------------------------------------------------
75 |
76 | **Attention**! The Alpine Linux comes with Busybox but its functionality as a shell and as a few emulated tools ``is not enough`` for TrivialRC. To work in this distribution it requires two extra packages: ``bash`` and ``procps``.
77 | As a result, Dockerfile for the `alpine:latest`_ base image would look like:
78 |
79 | .. code-block:: bash
80 |
81 | FROM alpine:latest
82 |
83 | RUN apk add --no-cache bash procps
84 |
85 | RUN wget -qP /etc/ http://trivialrc.vorakl.com/trc && \
86 | ( cd /etc && wget -qO - http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \
87 | chmod +x /etc/trc && \
88 | /etc/trc --version
89 |
90 | # Uncomment this if you have configuration files in trc.d/
91 | # COPY trc.d/ /etc/trc.d/
92 |
93 | ENTRYPOINT ["/etc/trc"]
94 |
95 |
96 | How to get started?
97 | ===================
98 |
99 | To get started and find out some features, basically, I suggest to go through `all available examples`_ and read their readmes plus comments along the code but to start from `one-liners`_ which show most common use cases and features.
100 |
101 | Command line options
102 | ====================
103 |
104 | It is important to notice that the order of command line options **is not** equal to their run order.
105 | In general it looks like:
106 |
107 | .. code-block:: bash
108 |
109 | $ trc [-h|--help] [-v|--version] [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [command [args]]
110 |
111 |
112 | Where
113 |
114 | * ``-h`` or ``--help``, prints a short help message
115 | * ``-v`` or ``--version``, prints a current version
116 | * ``-w 'directory'`` or ``--workdir 'directory'``, sets a location with configuration files
117 | * ``-B 'command1; command2; ...'``, boot commands
118 | * ``-H 'command1; command2; ...'``, halt commands
119 | * ``-D 'command1; command2; ...'``, async commands
120 | * ``-F 'command1; command2; ...'``, sync commands
121 | * ``command [args]``, a sync command
122 |
123 | So, command line options have to be supplied in the next order
124 |
125 | 1. ``-B``, zero or more
126 | 2. ``-H``, zero or more
127 | 3. ``-D``, zero or more
128 | 4. ``-F``, zero or more
129 | 5. ``command with arguments`` (without an option), zero or only one
130 |
131 | Examples:
132 |
133 | .. code-block:: bash
134 |
135 | $ trc -B 'name=$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, ${name}!'
136 |
137 | $ RC_WAIT_POLICY=wait_all trc -D 'echo Hello' -D 'sleep 2; echo World' echo waiting...
138 |
139 | $ RC_VERBOSE=true trc -F 'echo -n "Hello "; echo World'
140 |
141 | $ trc --workdir /opt/app
142 |
143 |
144 | Run stages
145 | ==========
146 |
147 | The life cycle of TrivialRC consists of different stages, with different isolation.
148 | By default, all configuration files (or trc.d/ directory with them) are searched in the directory from which was executed ``trc`` itself. For instance, if you've installed trc in /usr/bin/ and run it by using only its name, like ``trc``, then configuration will also be searched in /usr/bin/. Though, you can place configuration files anywhere you like and specify their location in the ``-w|--workdir`` option, like ``trc -w /etc/``.
149 |
150 | Let's check:
151 |
152 | .. code-block:: bash
153 |
154 | $ which trc
155 | /usr/bin/trc
156 |
157 | $ trc -B 'echo $work_dir'
158 | /usr/bin
159 |
160 | $ trc -w /etc -B 'echo $work_dir'
161 | /etc
162 |
163 |
164 | All stages are executed through in the next order:
165 |
166 | 1. ``boot``
167 | **Execution order**: trc.boot.* -> trc.d/boot.* -> [-B 'cmds' [...]]
168 |
169 | Commands run in a same environment as the main process and that's why it has to be used with caution.
170 | It's useful for setting up global variables which are seen in all other isolated environments.
171 | 2. ``async``
172 | **Execution order**: trc.async.* -> trc.d/async.* -> [-D 'cmds' [...]]
173 |
174 | Commands run in the separate environment, asynchronously (all run in parallel), in the background and do not affect the main process.
175 | If you are going to run more than one async commands, don't forget that default RC_WAIT_POLICY is set to 'wait_any' and the executing process will be stopped after the first finished command and only if there wasn't any running foreground (sync) command that could block the reaction on the TERM signal. So, there are two options:
176 |
177 | * to wait until all async commands have finished, you need to set RC_WAIT_POLICY to 'wait_all'.
178 | * to wait for the first finished command, do not change the default value of RC_WAIT_POLICY but run only async commands.
179 | 3. ``sync``
180 | **Execution order**: trc.sync.* -> trc.d/sync.* -> [-F 'cmds' [...]] -> [cmd]
181 |
182 | Commands run in the separate environment, synchronously (one by one), in the foreground and do not affect the main process.
183 | if you are going to run more than one sync commands, don't forget to change RC_WAIT_POLICY to 'wait_all' or 'wait_err', otherwise, the executing process will be stopped after the first command.
184 | 4. ``halt``
185 | **Execution order**: trc.halt.* -> trc.d/halt.* -> [-H 'cmds' [...]]
186 |
187 | Commands run in the separate environment, synchronously (one by one) when the main process is finishing (on exit).
188 | An exit status from the last halt command has precedence under an exit status from the main process which was supplied as ${_exit_status} variable. So you are able to keep a main exit status (by finishing as **exit ${_exit_status}**) or rewrite it to something else but anyway, if you have at least one halt command, TrivialRC will finish with an exit status of this halt command.
189 |
190 |
191 | Wait policies
192 | =============
193 |
194 | The rc system reacts differently when one of controlled processes finishes.
195 | Depending on the value of **RC_WAIT_POLICY** environment variable it makes a decision when exactly it should stop itself.
196 | The possible values are:
197 |
198 | * ``wait_all``
199 | stops after exiting all commands and it doesn't matter whether they are synchronous or asynchronous. Just keep in mind, if you need to catch a signal in the main process, it doesn't have to be blocked by some foreground (sync) process. For example, this mode can be helpful if you need to troubleshoot a container (with `wait_any` policy) where some async task fails and the whole container gets stopped by this immediately. In this case, you can change a policy to `wait_all` and run BASH in the foreground like ``docker -e RC_WAIT_POLICY=wait_all some-container bash``
200 | * ``wait_any`` [default]
201 | stops after exiting any of background commands and if there are no foreground commands working at that moment. It makes sense to use this mode if all commands are **asynchronous** (background). For example, if you need to start more than one process in the docker container, they all have to be asynchronous. Then, the main processed will be able to catch signals (for instance, from a docker daemon) and wait for finishing all other async processes.
202 | * ``wait_err``
203 | stops after the first failed command. It make sense to use this mode with **synchronous** (foreground) commands only. For example, if you need to iterate sequentially over the list of commands and to stop only if one of them has failed.
204 | * ``wait_forever``
205 | there is a special occasion when a process has doubled forked to become a daemon, it's still running but for the parent shell such process is considered as finished. So, in this mode, TrivialRC will keep working even if all processes have finished and it has to be stopped by the signal from its parent process (such as docker daemon for example).
206 |
207 |
208 | Verbose levels
209 | ==============
210 |
211 | By default, TrivailRC doesn't print any service messages at all.
212 | It only sends ``stdout`` and ``stderr`` of all isolated sub-shells to the same terminal.
213 | If another behavior is needed, you can redirect any of them inside each sub-shell separately.
214 | To increase the verbosity of rc system there are provided a few environment variables:
215 |
216 | * ``RC_DEBUG`` (true|false) [false]
217 | Prints out all commands which are being executed
218 | * ``RC_VERBOSE`` (true|false) [false]
219 | Prints out service information
220 | * ``RC_VERBOSE_EXTRA`` (true|false) [false]
221 | Prints out additional service information
222 |
223 |
224 | Integrated functions
225 | ====================
226 |
227 | You can also use some of internal functions in async/sync tasks:
228 |
229 | * ``say``
230 | prints only if RC_VERBOSE is set
231 | * ``log``
232 | does the same as ``say`` but add additional info about time, PID, namespace, etc
233 | * ``warn``
234 | does the say as ``log`` but sends a mesage to stderr
235 | * ``err``
236 | does the same as ``warn`` but exits with an error (exit status = 1)
237 | * ``debug``
238 | does the same as ``log`` but only if RC_VERBOSE_EXTRA is set
239 | * ``run``
240 | launches builtin or external commands without checking functions with the same name
241 | For instance, if you wanna run only external command from the standart PATH list, use ``run -p 'command'``
242 | Or, if you need to check existence of the command, try ``run -v 'command'``
243 |
244 |
245 | Useful global variables
246 | =======================
247 |
248 | * ``MAINPID``, for sending signals to the main process (see `Testing of Docker images`_)
249 | * ``_exit_status``, for checking or rewriting an exit status of the whole script (see `Process Manager`_, `Service Discovery`_)
250 |
251 | .. Links
252 |
253 | .. |build-status| image:: https://travis-ci.org/vorakl/TrivialRC.svg?branch=master
254 | :target: https://travis-ci.org/vorakl/TrivialRC
255 | :alt: Travis CI: continuous integration status
256 | .. _dumb-init: https://github.com/Yelp/dumb-init
257 | .. _tini: https://github.com/krallin/tini
258 | .. _Supervisor: https://github.com/Supervisor/supervisor
259 | .. _`centos:latest`: https://hub.docker.com/_/centos/
260 | .. _`alpine:latest`: https://hub.docker.com/_/alpine/
261 | .. _`all available examples`: https://github.com/vorakl/TrivialRC/tree/master/examples
262 | .. _`one-liners`: https://github.com/vorakl/TrivialRC/blob/master/examples/one-liners
263 | .. _`Testing of Docker images`: https://github.com/vorakl/TrivialRC/tree/master/examples/reliable-tests-for-docker-images
264 | .. _`Process Manager`: https://github.com/vorakl/TrivialRC/blob/master/examples/process-manager/trc.d/halt.remove-logs
265 | .. _`Service Discovery`: https://github.com/vorakl/TrivialRC/blob/master/examples/docker-service-discovery/trc.d/halt.sd-unreg
266 | .. _subscribe: https://www.freelists.org/list/trivialrc-dev
267 |
--------------------------------------------------------------------------------
/docs/404.html:
--------------------------------------------------------------------------------
1 |
The minimalistic Run-time Configuration (RC) system and process manager
--------------------------------------------------------------------------------
/docs/pages/info/index.html:
--------------------------------------------------------------------------------
1 | The minimalistic Run-time Configuration (RC) system and process manager :: TrivialRC
The minimalistic Run-time Configuration (RC) system and process manager is written in pure BASH and uses just a few external utilities like ls, ps, date and sleep. Minimally, installation of TrivialRC consists of only one file which can be downloaded directly from the Github. Originaly, it was designed for use in containers but it also can be well used for running a group of processes asynchronously and synchronously, as well as managing their running order and exit codes.
TrivialRC is not a replacement for an init process that usually resides in /sbin/init and has a PID 1. In containers for this purpose projects like dumb-init or tini can be used, although in most cases, having only TrivialRC as a first/main process (PID 1) in containers is quite enough. In terms of Docker, the best place for it is ENTRYPOINT.
TrivialRC is an equivalent to well known /etc/rc for xBSD users. The RC system that is used for managing startup and shutdown processes. It can start and stop one or more processes, in parallel or sequentially, on back- or foreground, react differently in case of process failures, etc. All commands can be specified in the command line if they are relatively simple, or in separate files if a more comprehensive scenario is needed. That's why it can be used as a simplest tool for managing a group of process and be a lightweight replacement for solutions like Supervisor.
For instance, in docker images when TrivialRC is used as an Entrypoint, it doesn't reveal itself by default, does not affect any configuration and behaves absolutely transparently. So, you can add it into any Dockerfiles which do not have an entrypoint yet and get by this the full control under processes with fairly detailed logs of what's is going on inside a container. Please, take a look at examples for more information.
Installation
Basically, all you need to install TrivialRC is download the latest release of the script from http://trivialrc.vorakl.com/trc and give it an execute permission. By default, it looks for configuration files in the same directory from which it was invoked but this behavior can be changed by setting a work directory (-w|--workdir parametr). So, if you are going to use configuration files and keep them in /etc/, then you would probably want to install the script to /etc/ as well and simply run it without specifying any parametrs.
Another option in this case could be to install the script in a more appropriate path but don't forget to specify --workdir /etc parametr every time when you invoke this rc system. Both options are possible and depend more on a particular use case. For instance, in case of using in a docker container, I personaly prefer to have all configuration in separate files in trc.d/ sub-directory and copy it together with the script in /etc/.
The installation on top of CentOS Linux base image
This is an example of how it would look in a Dockerfile with centos:latest as base image:
FROM centos:latest
2 |
3 | RUN curl -sSLfo /etc/trc http://trivialrc.vorakl.com/trc &&\
4 | (cd /etc && curl -sSLf http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c )&&\
5 | chmod +x /etc/trc &&\
6 | /etc/trc --version
7 |
8 | # Uncomment this if you have configuration files in trc.d/
9 | # COPY trc.d/ /etc/trc.d/
10 |
11 | ENTRYPOINT ["/etc/trc"]
12 |
The installation on top of Alpine Linux base image
Attention! The Alpine Linux comes with Busybox but its functionality as a shell and as a few emulated tools is not enough for TrivialRC. To work in this distribution it requires two extra packages: bash and procps. As a result, Dockerfile for the alpine:latest base image would look like:
FROM alpine:latest
13 |
14 | RUN apk add --no-cache bash procps
15 |
16 | RUN wget -qP /etc/ http://trivialrc.vorakl.com/trc &&\
17 | (cd /etc && wget -qO - http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c )&&\
18 | chmod +x /etc/trc &&\
19 | /etc/trc --version
20 |
21 | # Uncomment this if you have configuration files in trc.d/
22 | # COPY trc.d/ /etc/trc.d/
23 |
24 | ENTRYPOINT ["/etc/trc"]
25 |
How to get started?
To get started and find out some features, basically, I suggest to go through all available examples and read their readmes plus comments along the code but to start from one-liners which show most common use cases and features.
Command line options
It is important to notice that the order of command line options is not equal to their run order. In general it looks like:
The life cycle of TrivialRC consists of different stages, with different isolation. By default, all configuration files (or trc.d/ directory with them) are searched in the directory from which was executed trc itself. For instance, if you've installed trc in /usr/bin/ and run it by using only its name, like trc, then configuration will also be searched in /usr/bin/. Though, you can place configuration files anywhere you like and specify their location in the -w|--workdir option, like trc -w /etc/.
Commands run in a same environment as the main process and that's why it has to be used with caution. It's useful for setting up global variables which are seen in all other isolated environments.
Commands run in the separate environment, asynchronously (all run in parallel), in the background and do not affect the main process. If you are going to run more than one async commands, don't forget that default RC_WAIT_POLICY is set to 'wait_any' and the executing process will be stopped after the first finished command and only if there wasn't any running foreground (sync) command that could block the reaction on the TERM signal. So, there are two options:
to wait until all async commands have finished, you need to set RC_WAIT_POLICY to 'wait_all'.
to wait for the first finished command, do not change the default value of RC_WAIT_POLICY but run only async commands.
Commands run in the separate environment, synchronously (one by one), in the foreground and do not affect the main process. if you are going to run more than one sync commands, don't forget to change RC_WAIT_POLICY to 'wait_all' or 'wait_err', otherwise, the executing process will be stopped after the first command.
Commands run in the separate environment, synchronously (one by one) when the main process is finishing (on exit). An exit status from the last halt command has precedence under an exit status from the main process which was supplied as ${_exit_status} variable. So you are able to keep a main exit status (by finishing as exit ${_exit_status}) or rewrite it to something else but anyway, if you have at least one halt command, TrivialRC will finish with an exit status of this halt command.
Wait policies
The rc system reacts differently when one of controlled processes finishes. Depending on the value of RC_WAIT_POLICY environment variable it makes a decision when exactly it should stop itself. The possible values are:
wait_all
stops after exiting all commands and it doesn't matter whether they are synchronous or asynchronous. Just keep in mind, if you need to catch a signal in the main process, it doesn't have to be blocked by some foreground (sync) process. For example, this mode can be helpful if you need to troubleshoot a container (with wait_any policy) where some async task fails and the whole container gets stopped by this immediately. In this case, you can change a policy to wait_all and run BASH in the foreground like docker -e RC_WAIT_POLICY=wait_all some-container bash
wait_any [default]
stops after exiting any of background commands and if there are no foreground commands working at that moment. It makes sense to use this mode if all commands are asynchronous (background). For example, if you need to start more than one process in the docker container, they all have to be asynchronous. Then, the main processed will be able to catch signals (for instance, from a docker daemon) and wait for finishing all other async processes.
wait_err
stops after the first failed command. It make sense to use this mode with synchronous (foreground) commands only. For example, if you need to iterate sequentially over the list of commands and to stop only if one of them has failed.
wait_forever
there is a special occasion when a process has doubled forked to become a daemon, it's still running but for the parent shell such process is considered as finished. So, in this mode, TrivialRC will keep working even if all processes have finished and it has to be stopped by the signal from its parent process (such as docker daemon for example).
Verbose levels
By default, TrivailRC doesn't print any service messages at all. It only sends stdout and stderr of all isolated sub-shells to the same terminal. If another behavior is needed, you can redirect any of them inside each sub-shell separately. To increase the verbosity of rc system there are provided a few environment variables:
RC_DEBUG (true|false) [false]
Prints out all commands which are being executed
RC_VERBOSE (true|false) [false]
Prints out service information
RC_VERBOSE_EXTRA (true|false) [false]
Prints out additional service information
Integrated functions
You can also use some of internal functions in async/sync tasks:
say
prints only if RC_VERBOSE is set
log
does the same as say but add additional info about time, PID, namespace, etc
warn
does the say as log but sends a mesage to stderr
err
does the same as warn but exits with an error (exit status = 1)
debug
does the same as log but only if RC_VERBOSE_EXTRA is set
run
launches builtin or external commands without checking functions with the same name For instance, if you wanna run only external command from the standart PATH list, use run -p 'command' Or, if you need to check existence of the command, try run -v 'command'
--------------------------------------------------------------------------------
/docs/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /unknown/
3 |
4 | Sitemap: https://trivialrc.cf/sitemap.xml
5 |
--------------------------------------------------------------------------------
/docs/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | https://trivialrc.vorakl.com/
5 | daily
6 |
7 |
8 | https://trivialrc.vorakl.com/pages/info/
9 | weekly
10 | 2023-12-19
11 |
12 |
--------------------------------------------------------------------------------
/docs/theme/bootstrap.min.responsive.css:
--------------------------------------------------------------------------------
1 |
2 | .hidden{display:none;visibility:hidden;}
3 | @media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:768px){.container{width:auto;padding:0 20px;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;}}@media (min-width:768px) and (max-width:980px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .span1{width:42px;} .span2{width:104px;} .span3{width:166px;} .span4{width:228px;} .span5{width:290px;} .span6{width:352px;} .span7{width:414px;} .span8{width:476px;} .span9{width:538px;} .span10{width:600px;} .span11{width:662px;} .span12,.container{width:724px;} .offset1{margin-left:82px;} .offset2{margin-left:144px;} .offset3{margin-left:206px;} .offset4{margin-left:268px;} .offset5{margin-left:330px;} .offset6{margin-left:392px;} .offset7{margin-left:454px;} .offset8{margin-left:516px;} .offset9{margin-left:578px;} .offset10{margin-left:640px;} .offset11{margin-left:702px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.801104972%;} .row-fluid .span2{width:14.364640883%;} .row-fluid .span3{width:22.928176794%;} .row-fluid .span4{width:31.491712705%;} .row-fluid .span5{width:40.055248616%;} .row-fluid .span6{width:48.618784527%;} .row-fluid .span7{width:57.182320438000005%;} .row-fluid .span8{width:65.74585634900001%;} .row-fluid .span9{width:74.30939226%;} .row-fluid .span10{width:82.87292817100001%;} .row-fluid .span11{width:91.436464082%;} .row-fluid .span12{width:99.999999993%;} input.span1,textarea.span1,.uneditable-input.span1{width:32px;} input.span2,textarea.span2,.uneditable-input.span2{width:94px;} input.span3,textarea.span3,.uneditable-input.span3{width:156px;} input.span4,textarea.span4,.uneditable-input.span4{width:218px;} input.span5,textarea.span5,.uneditable-input.span5{width:280px;} input.span6,textarea.span6,.uneditable-input.span6{width:342px;} input.span7,textarea.span7,.uneditable-input.span7{width:404px;} input.span8,textarea.span8,.uneditable-input.span8{width:466px;} input.span9,textarea.span9,.uneditable-input.span9{width:528px;} input.span10,textarea.span10,.uneditable-input.span10{width:590px;} input.span11,textarea.span11,.uneditable-input.span11{width:652px;} input.span12,textarea.span12,.uneditable-input.span12{width:714px;}}@media (max-width:675px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .span1{width:70px;} .span2{width:170px;} .span3{width:270px;} .span4{width:370px;} .span5{width:470px;} .span6{width:570px;} .span7{width:670px;} .span8{width:770px;} .span9{width:870px;} .span10{width:970px;} .span11{width:1070px;} .span12,.container{width:1170px;} .offset1{margin-left:130px;} .offset2{margin-left:230px;} .offset3{margin-left:330px;} .offset4{margin-left:430px;} .offset5{margin-left:530px;} .offset6{margin-left:630px;} .offset7{margin-left:730px;} .offset8{margin-left:830px;} .offset9{margin-left:930px;} .offset10{margin-left:1030px;} .offset11{margin-left:1130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.982905983%;} .row-fluid .span2{width:14.529914530000001%;} .row-fluid .span3{width:23.076923077%;} .row-fluid .span4{width:31.623931624%;} .row-fluid .span5{width:40.170940171000005%;} .row-fluid .span6{width:48.717948718%;} .row-fluid .span7{width:57.264957265%;} .row-fluid .span8{width:65.81196581200001%;} .row-fluid .span9{width:74.358974359%;} .row-fluid .span10{width:82.905982906%;} .row-fluid .span11{width:91.45299145300001%;} .row-fluid .span12{width:100%;} input.span1,textarea.span1,.uneditable-input.span1{width:60px;} input.span2,textarea.span2,.uneditable-input.span2{width:160px;} input.span3,textarea.span3,.uneditable-input.span3{width:260px;} input.span4,textarea.span4,.uneditable-input.span4{width:360px;} input.span5,textarea.span5,.uneditable-input.span5{width:460px;} input.span6,textarea.span6,.uneditable-input.span6{width:560px;} input.span7,textarea.span7,.uneditable-input.span7{width:660px;} input.span8,textarea.span8,.uneditable-input.span8{width:760px;} input.span9,textarea.span9,.uneditable-input.span9{width:860px;} input.span10,textarea.span10,.uneditable-input.span10{width:960px;} input.span11,textarea.span11,.uneditable-input.span11{width:1060px;} input.span12,textarea.span12,.uneditable-input.span12{width:1160px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}}
4 |
--------------------------------------------------------------------------------
/docs/theme/icons/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/docs/theme/icons/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/docs/theme/icons/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/docs/theme/icons/glyphicons-halflings.png
--------------------------------------------------------------------------------
/docs/theme/local.min.css:
--------------------------------------------------------------------------------
1 | /* Styling for Nav bar. Bg color, link color etc */
2 | .navbar-inner{padding-left:20px;padding-right:20px;background-color:#2E435E;background-image:-moz-linear-gradient(top,#2e435e,#205081);background-image:-ms-linear-gradient(top,#2e435e,#205081);background-image:-webkit-gradient(linear,0 0,0 100%,from(#2e435e),to(#205081));background-image:-webkit-linear-gradient(top,#2e435e,#205081);background-image:-o-linear-gradient(top,#2e435e,#205081);background-image:linear-gradient(top,#2e435e,#205081);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333',endColorstr='#222222',GradientType=0);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1)}.navbar .brand:hover{color:#49AFCD}.navbar .brand{padding:12px 20px 12px}.navbar .nav > li > a{color:#FFF;padding:12px 10px 12px}.navbar .nav > li > a:hover{color:#49AFCD}.navbar .divider-vertical{border-right:1px solid #4D4D4D}.navbar{margin-bottom:4em}body{font-size:1em}p{font-size:1em;line-height:1.6em;margin-top:0.2em;margin-bottom:0.2em}.p-footer{font-size:0.7em;line-height:1.6em;padding-bottom:2em}li{line-height:1.6em}h1,h2,h3,h4,h5,h6{padding:2px 0 2px;text-align:justify}.table th,.table td{line-height:1.3em}a:hover{text-decoration:underline}.article{padding-bottom:20px}.content-title{margin-bottom:30px;text-align:justify}.content p{text-align:justify;font-size:1.1rem}.content dd{text-align:justify}.article a:hover{text-decoration:underline}.iconmenu{margin-right:0.4em}.well.small{padding:6px 6px 4px;text-align:justify}.btn-info.xsmall{padding:1px 3px 1px;font-size:11px;float:right;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.btn-info.xsmall.left{padding:1px 3px 1px;font-size:11px;float:left;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.img.right{float:right;padding:10px 15px 10px 15px}.summary{margin-bottom:3em}.summary pre{display:none}.well{margin-bottom:5px}
3 |
--------------------------------------------------------------------------------
/docs/theme/pygments.min.css:
--------------------------------------------------------------------------------
1 | .highlight pre .hll{background-color:#ffc}.highlight pre{background:#f0f3f3}.highlight pre .c{color:#09f;font-style:italic}.highlight pre .err{color:#a00;background-color:#faa}.highlight pre .k{color:#069;font-weight:700}.highlight pre .o{color:#555}.highlight pre .cm{color:#09f;font-style:italic}.highlight pre .cp{color:#099}.highlight pre .c1{color:#09f;font-style:italic}.highlight pre .cs{color:#09f;font-weight:700;font-style:italic}.highlight pre .gd{background-color:#fcc;border:1px solid #c00}.highlight pre .ge{font-style:italic}.highlight pre .gr{color:red}.highlight pre .gh{color:#030;font-weight:700}.highlight pre .gi{background-color:#cfc;border:1px solid #0c0}.highlight pre .go{color:#aaa}.highlight pre .gp{color:#009;font-weight:700}.highlight pre .gs{font-weight:700}.highlight pre .gu{color:#030;font-weight:700}.highlight pre .gt{color:#9c6}.highlight pre .kc{color:#069;font-weight:700}.highlight pre .kd{color:#069;font-weight:700}.highlight pre .kn{color:#069;font-weight:700}.highlight pre .kp{color:#069}.highlight pre .kr{color:#069;font-weight:700}.highlight pre .kt{color:#078;font-weight:700}.highlight pre .m{color:#f60}.highlight pre .s{color:#c30}.highlight pre .na{color:#309}.highlight pre .nb{color:#366}.highlight pre .nc{color:#0a8;font-weight:700}.highlight pre .no{color:#360}.highlight pre .nd{color:#99f}.highlight pre .ni{color:#999;font-weight:700}.highlight pre .ne{color:#c00;font-weight:700}.highlight pre .nf{color:#c0f}.highlight pre .nl{color:#99f}.highlight pre .nn{color:#0cf;font-weight:700}.highlight pre .nt{color:#309;font-weight:700}.highlight pre .nv{color:#033}.highlight pre .ow{color:#000;font-weight:700}.highlight pre .w{color:#bbb}.highlight pre .mb{color:#f60}.highlight pre .mf{color:#f60}.highlight pre .mh{color:#f60}.highlight pre .mi{color:#f60}.highlight pre .mo{color:#f60}.highlight pre .sb{color:#c30}.highlight pre .sc{color:#c30}.highlight pre .sd{color:#c30;font-style:italic}.highlight pre .s2{color:#c30}.highlight pre .se{color:#c30;font-weight:700}.highlight pre .sh{color:#c30}.highlight pre .si{color:#a00}.highlight pre .sx{color:#c30}.highlight pre .sr{color:#3aa}.highlight pre .s1{color:#c30}.highlight pre .ss{color:#fc3}.highlight pre .bp{color:#366}.highlight pre .vc{color:#033}.highlight pre .vg{color:#033}.highlight pre .vi{color:#033}.highlight pre .il{color:#f60}
2 |
--------------------------------------------------------------------------------
/docs/trc:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # TrivialRC
4 | # The minimalistic RC system and process manager for containers and applications
5 | # Copyright (c) 2016, 2017 by vorakl
6 |
7 | trc_version="v1.1.12"
8 |
9 | set +e # Do not exit on errors by default
10 | if [[ "${RC_DEBUG}" = "true" ]]; then
11 | set -x # Turns on Debug mode
12 | fi
13 |
14 | main() {
15 | check_ver_usage "$1"
16 | check_req_bins ls ps pkill sleep date basename dirname
17 |
18 | # It's possible to exit from the last `halt` command with ${_exit_status} which comes from main()
19 | # and rewrite by this an exit status of the `halt` stage which always has a priority against main's exit status
20 | trap '_exit_status=$?; trap '_ignore_mt_ex=1' EXIT; debug "exit-trap (exitcode=${_exit_status})"; hook_main_exit' EXIT
21 | trap '_es_mt_s=$?; trap '_ignore_mt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=${_es_mt_s})"; exit ${_es_mt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
22 | trap '_es_mt_er=$?; trap '_ignore_mt_er=1' ERR; if [[ "${RC_WAIT_POLICY}" = "wait_err" ]]; then debug "err-trap (exitcode=${_es_mt_er})"; exit ${_es_mt_er}; fi' ERR
23 |
24 | local _wait_bg_cmd _file _es_m_f=0 _es_m_d=0
25 |
26 | export SELF_NAME=$(run -p basename $0 .sh) # Self name for logging purpose
27 | export MAINPID="${BASHPID}" # PID of the main process
28 | : ${RC_WAIT_POLICY:=wait_any}; export RC_WAIT_POLICY
29 |
30 | work_dir="${PWD}"
31 | boot_stage_left=0
32 | halt_cmds_exist=0
33 | halt_cmds=()
34 | child_pids="" # Collects of child processes
35 | ns="main" # Name Space
36 |
37 | case "${RC_WAIT_POLICY}" in
38 | wait_all) _wait_bg_cmd="run wait";;
39 | wait_any) _wait_bg_cmd="run wait";;
40 | wait_err) _wait_bg_cmd="run wait";;
41 | *) _wait_bg_cmd=":";;
42 | esac
43 |
44 | log "The wait policy: ${RC_WAIT_POLICY}"
45 |
46 | if [[ "$1" = "-w" ]] || [[ "$1" = "--workdir" ]]; then
47 | shift
48 | work_dir="$1"
49 | shift
50 | fi
51 | if [[ -n "${RC_WORK_DIR}" ]]; then
52 | work_dir="${RC_WORK_DIR}"
53 | else
54 | RC_WORK_DIR="${work_dir}"
55 | export RC_WORK_DIR
56 | fi
57 | log "Work dir: ${RC_WORK_DIR}"
58 |
59 | debug "Looking for \`boot\` scripts and commands..."
60 | {
61 | # Reads commands from files to run before everything
62 | for _file in $(run -p ls ${work_dir}/trc.boot.* ${work_dir}/trc.d/boot.* 2>/dev/null || true); do
63 | log "Launching in the $(ns_long ${ns}): ${_file}"
64 | set -e
65 | . ${_file}
66 | set +e
67 | done
68 |
69 | # Checks for boot tasks in the command line (all commands and their parameters should be supplied as one parameter)
70 | while [[ "$1" = "-B" ]]; do
71 | shift
72 | log "Launching in the $(ns_long ${ns}): $1"
73 | set -e
74 | eval "$1"
75 | set +e
76 | shift
77 | done
78 | }
79 | boot_stage_left=1
80 |
81 | debug "Looking for \`halt\` commands..."
82 | # Collects commands for the `halt` stage from a command line to be run on exit from the main exit trap
83 | while [[ "$1" = "-H" ]]; do
84 | shift
85 | halt_cmds_exist=1
86 | halt_cmds[${#halt_cmds[*]}]="$1"
87 | shift
88 | done
89 |
90 | debug "Looking for \`async\` scripts and commands..."
91 | # Reads commands from files to run in the background (in parallel)
92 | for _file in $(run -p ls ${work_dir}/trc.async.* ${work_dir}/trc.d/async.* 2>/dev/null || true); do
93 | (
94 | # Run this on any exit, catching the exitcode of the "main" command,
95 | # printing additional info and finishing up this sub-program with the right exitcode
96 | trap '_es_dt_ex=$?; trap '_ignore_mt_ex=1' EXIT; debug "exit-trap (exitcode=${_es_dt_ex})"; hook_sub_exit ${_es_dt_ex} "${_file}"' EXIT
97 |
98 | # In case of exit on errors (set -e) or by a signal, catch exitcode and exit with it, which
99 | # will lead to triggering an EXIT trap
100 | trap '_es_dt_s=$?; trap '_ignore_dt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_dt_s)"; exit ${_es_dt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
101 | trap '_es_dt_er=$?; trap '_ignore_dt_er=1' ERR; debug "err-trap (exitcode=$_es_dt_er)"; exit ${_es_dt_er}' ERR
102 |
103 | ns="async"
104 | log "Launching in the $(ns_long ${ns}): ${_file}"
105 | . ${_file}
106 | )&
107 | child_pids="${child_pids} $!"
108 | done
109 |
110 | # Checks for background tasks in the command line (all commands and their parameters should be supplied as one parameter)
111 | while [[ "$1" = "-D" ]]; do
112 | shift
113 | (
114 | trap '_es_dt_ex=$?; trap '_ignore_dt_ex=1' EXIT; debug "exit-trap (exitcode=${_es_dt_ex})"; hook_sub_exit ${_es_dt_ex} "$1"' EXIT
115 | trap '_es_dt_s=$?; trap '_ignore_dt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_dt_s)"; exit ${_es_dt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
116 | trap '_es_dt_er=$?; trap '_ignore_dt_er=1' ERR; debug "err-trap (exitcode=$_es_dt_er)"; exit ${_es_dt_er}' ERR
117 |
118 | ns="async"
119 | log "Launching in the $(ns_long ${ns}): $1"
120 | eval "$1"
121 | )&
122 | child_pids="${child_pids} $!"
123 | shift
124 | done
125 |
126 | debug "Looking for \`sync\` scripts and commands..."
127 | # Checks for foreground tasks in files (sequentially)
128 | for _file in $(run -p ls ${work_dir}/trc.sync.* ${work_dir}/trc.d/sync.* 2>/dev/null || true); do
129 | (
130 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "${_file}"' EXIT
131 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
132 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR
133 |
134 | ns="sync"
135 | log "Launching in the $(ns_long ${ns}): ${_file}"
136 | . ${_file}
137 | )
138 | # Catch the exitcode of a foreground sub-program
139 | _es_m_f=$?
140 | done
141 |
142 | # Checks for foreground tasks in the command line (all commands and their parameters should be supplied as one parameter)
143 | while [[ "$1" = "-F" ]]; do
144 | shift
145 | (
146 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "$1"' EXIT
147 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
148 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR
149 |
150 | ns="sync"
151 | log "Launching in the $(ns_long ${ns}): $1"
152 | eval "$1"
153 | )
154 | _es_m_f=$?
155 | shift
156 | done
157 |
158 | # Checks for a foreground task in the command line (without any quotation!)
159 | if [[ -n "$*" ]]; then
160 | (
161 | set -e
162 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "$@"' EXIT
163 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
164 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR
165 |
166 | ns="sync"
167 | log "Launching in the $(ns_long ${ns}): $@"
168 | "$@"
169 | )
170 | # Catch the exitcode of a foreground sub-program
171 | _es_m_f=$?
172 | fi
173 |
174 | # Wait for all background processes and exit with a status of the last one
175 | # or with 128+SIGNAL in case of getting a signal
176 | # this is a work around for not trigerring ERR trap in main() two times in case of non 0 exist status
177 | ${_wait_bg_cmd} ${child_pids} || _es_m_d=$?
178 |
179 | if [ ${_es_m_d} -ne 0 ]; then
180 | _es_m_f=${_es_m_d} # update exit code only if one of background processes has failed
181 | fi
182 |
183 | if [[ "${RC_WAIT_POLICY}" = "wait_forever" ]]; then
184 | infinite_loop
185 | fi
186 |
187 | return ${_es_m_f}
188 | }
189 |
190 | say() {
191 | if [[ "${RC_VERBOSE}" = "true" ]]; then
192 | run echo "$@"
193 | fi
194 | }
195 |
196 | log() {
197 | say "$(run -p date '+%Y-%m-%d %H:%M:%S') ${SELF_NAME} [${ns}/${BASHPID}]: $@"
198 | }
199 |
200 | warn() {
201 | log "$@" >&2
202 | }
203 |
204 | err() {
205 | warn "$@"
206 | exit 1
207 | }
208 |
209 | debug() {
210 | if [[ "${RC_VERBOSE_EXTRA}" = "true" ]]; then
211 | log " - $@"
212 | fi
213 | }
214 |
215 | hook_main_exit() {
216 | local _pid _cpids _file _cmd _es_mtt_h=0 _term_timeout=5
217 |
218 | # If it fails in the `boot` stage, it appears here with 'set -e'. So, reset to +e again
219 | set +e
220 | trap 'true' ERR
221 |
222 | log "Trying to terminate sub-processes..."
223 | for _pid in ${child_pids}; do
224 | if run -p ps -p ${_pid} &> /dev/null; then
225 | log "terminating the child process "
226 |
227 | # Removes all unexpected sub-processes
228 | _cpids="$(run -p pgrep -P ${_pid} -d,)"
229 | run kill -TERM ${_pid} $(echo "${_cpids}" | tr ',' ' ') &> /dev/null
230 |
231 | while run -p ps -p ${_pid},${_cpids} &> /dev/null && [[ ${_term_timeout} -gt 0 ]]
232 | do
233 | run -p sleep 1
234 | : $((_term_timeout--))
235 | done
236 |
237 | if run -p ps -p ${_pid},${_cpids} &>/dev/null; then
238 | run kill -KILL ${_pid} $(run echo "${_cpids}" | run -p tr ',' ' ') &> /dev/null
239 | fi
240 | fi
241 | done
242 | debug "removing all unexpected sub-processes"
243 | if ! run -p pkill -KILL -P ${MAINPID} &> /dev/null; then
244 | true
245 | fi
246 |
247 | # --- `halt`
248 | if [[ boot_stage_left -eq 1 ]]; then
249 | debug "Looking for \`halt\` scripts..."
250 | # Checks for shutdown tasks in files (sequentially)
251 | for _file in $(run -p ls ${work_dir}/trc.halt.* ${work_dir}/trc.d/halt.* 2>/dev/null || true); do
252 | halt_cmds_exist=1
253 | (
254 | set -e # Exit on errors in the sub-shell
255 | ns="halt"
256 | log "Running the shutdown script: ${_file}"
257 | . ${_file}
258 | )
259 | _es_mtt_h=$?
260 | log "Exiting from the shutdown script (exitcode=${_es_mtt_h}): ${_file}"
261 | done
262 |
263 | # Runs shutdown tasks from the command line if any
264 | for _cmd in "${halt_cmds[@]}"; do
265 | (
266 | set -e # Exit on errors in the sub-shell
267 |
268 | ns="halt"
269 | log "Running the shutdown command: ${_cmd}"
270 | eval "${_cmd}"
271 | )
272 | _es_mtt_h=$?
273 | log "Exiting from the shutdown command (exitcode=${_es_mtt_h}): ${_cmd}"
274 | done
275 | fi
276 |
277 | if [[ ${halt_cmds_exist} -eq 1 ]]; then
278 | _exit_status=${_es_mtt_h}
279 | fi
280 |
281 | log "Exited (exitcode=${_exit_status})"
282 | exit ${_exit_status}
283 | }
284 |
285 | hook_sub_exit() {
286 | set +e # do not stop or errors anyway
287 |
288 | local _rc=$1 # Getting the exit code for a logging purpose only
289 | shift
290 |
291 | log "Exiting in the $(ns_long ${ns}) (exitcode=${_rc}): $@"
292 |
293 | if [[ "${RC_WAIT_POLICY}" = "wait_any" ]]; then
294 | # If exiting from a bg process and don't need to wait other processes, let's stop the main
295 | if run -p ps -p ${MAINPID} &> /dev/null; then
296 | debug "terminating the main process "
297 | run kill -TERM ${MAINPID} &> /dev/null
298 | fi
299 | fi
300 |
301 | exit ${_rc}
302 | }
303 |
304 | infinite_loop() {
305 | log "Activated infinite loop! To stop, press or send SIGTERM..."
306 | while true; do
307 | run -p sleep 1
308 | done
309 | }
310 |
311 | ns_long() {
312 | local _ns
313 |
314 | case "$1" in
315 | fg|sync) _ns="foreground";;
316 | bg|async) _ns="background";;
317 | sd|halt) _ns="shutdown";;
318 | main) _ns="boot";;
319 | *) _ns="$1";;
320 | esac
321 |
322 | echo "${_ns}"
323 | }
324 |
325 | check_ver_usage() {
326 | case "$1" in
327 | -v|--version) print_version ;;
328 | -h|--help) print_usage ;;
329 | *) : ;;
330 | esac
331 | }
332 |
333 | print_usage() {
334 | local _self=$0
335 | run echo "Usage: ${_self} [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [cmd [args]]"
336 | run echo "Examples:"
337 | run echo -e " $ ${_self} -B 'name=\$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, \${name}!'"
338 | run echo -e " $ RC_WAIT_POLICY=wait_all ${_self} -D 'echo Hello' -D 'sleep 2; echo World' echo waiting..."
339 | run echo -e " $ RC_VERBOSE=true ${_self} -F 'echo -n \"Hello \"; echo World'"
340 | run echo -e " $ ${_self} --workdir /opt/app"
341 | exit 0
342 | }
343 |
344 | print_version() {
345 | run echo "trc (TrivialRC) $trc_version"
346 | exit 0
347 | }
348 |
349 | run() {
350 | builtin command "$@"
351 | }
352 |
353 | check_req_bins() {
354 | local _cmd
355 |
356 | for _cmd in "$@"; do
357 | run -v ${_cmd} &> /dev/null || { echo "There is no the required command: ${_cmd}" >&2; exit 1; }
358 | done
359 | }
360 |
361 | main "$@"
362 |
--------------------------------------------------------------------------------
/docs/trc.sha256:
--------------------------------------------------------------------------------
1 | 35dff4c7a795bffae8e376585aec0cf1923760abfec677d87e58a74878ddedc7 trc
2 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # The TrivialRC's examples
2 |
3 | * [One-liners](https://github.com/vorakl/TrivialRC/blob/master/examples/one-liners/README.md) which show most common use cases and features
4 | * The example of using [configuration files](https://github.com/vorakl/TrivialRC/tree/master/examples/config-files) instead of command line parameters
5 | * A docker container that registers itself in a [Service Discovery](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-service-discovery) in the beginning, then starts some application and automatically removes the registration on exit
6 | * This example launches [two different applications](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-two-apps) inside one docker container and controls the availability both of them. If any of the applications has stopped working fore some reasons, the whole container will be stopped automatically with an appropriate exit status
7 | * The example of building [docker base images](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-base-images) with TrivialRC as an ENTRYPOINT
8 | * Another useful use case is using TrivialRC for [process managing](https://github.com/vorakl/TrivialRC/tree/master/examples/process-manager) of a group of processes which represent one compound application and can be invoked then on the system from the Sysdemd
9 | * This solution shows how to [configure services in a docker container by using templates](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-config-templates) and environment variables
10 | * This example goes even further and shows how easy to [download a configuration from remote resource at run-time](https://github.com/vorakl/docker-images/tree/master/centos-opensmtpd#download-configuration-at-run-time-from-a-remote-resource) in docker container
11 | * This trick shows how to [create configuration on the fly](https://github.com/vorakl/TrivialRC/tree/master/examples/self-configuring) from the `boot` stage
12 | * [Serial launching of a group of parallel processes](https://github.com/vorakl/TrivialRC/tree/master/examples/sync-run-of-async-groups) and failing immediately if some group failed
13 | * [Reliable tests](https://github.com/vorakl/TrivialRC/tree/master/examples/reliable-tests-for-docker-images) for docker images
14 |
15 |
--------------------------------------------------------------------------------
/examples/config-files/README.md:
--------------------------------------------------------------------------------
1 | # Keeping the configuration in files
2 |
3 | In these examples it will set a few variables and defining a new function in the `boot` mode.
4 | Then in the background (in the `async` mode) it will create a file using the new function and values of variables.
5 | In the next step, it will run in `sync` mode (in the foreground). Here, trc first sleeps for a defined number of seconds
6 | and then prints out the content of the file. In the last step, it will run in `halt` mode and remove previously created file.
7 | A required RC_WAIT_POLICY for this example is wait_all.
8 |
9 | ### The structure of `trc.d` directory
10 |
11 | ```bash
12 | $ ls -1 trc.d/
13 | async.info
14 | boot.funcs
15 | boot.setvars
16 | halt.delfile
17 | sync.1
18 | sync.2
19 | ```
20 |
21 | ### The execution order
22 |
23 | 1. boot.funcs
24 | 2. boot.setvars
25 | 3. async.info
26 | 4. sync.1
27 | 5. sync.2
28 | 6. halt.delfile
29 |
30 | ### Results
31 |
32 | * without logs
33 |
34 | ```bash
35 | $ RC_WAIT_POLICY=wait_all ./trc
36 | host: marche
37 | ip: 127.0.0.1
38 | login: vorakl
39 | ```
40 |
41 | * with logs
42 |
43 | ```bash
44 | $ RC_VERBOSE=true RC_WAIT_POLICY=wait_all ./trc
45 | 2017-02-23 01:04:16 trc [main/7207]: The wait policy: wait_all
46 | 2017-02-23 01:04:16 trc [main/7207]: Launching in the boot: ./trc.d/boot.funcs
47 | 2017-02-23 01:04:16 trc [main/7207]: Launching in the boot: ./trc.d/boot.setvars
48 | 2017-02-23 01:04:16 trc [async/7225]: Launching in the background: ./trc.d/async.info
49 | 2017-02-23 01:04:16 trc [sync/7231]: Launching in the foreground: ./trc.d/sync.1
50 | 2017-02-23 01:04:16 trc [async/7225]: Exiting in the background (exitcode=0): ./trc.d/async.info
51 | 2017-02-23 01:04:18 trc [sync/7231]: Exiting in the foreground (exitcode=0): ./trc.d/sync.1
52 | 2017-02-23 01:04:18 trc [sync/7244]: Launching in the foreground: ./trc.d/sync.2
53 | host: marche
54 | ip: 127.0.0.1
55 | login: vorakl
56 | 2017-02-23 01:04:18 trc [sync/7244]: Exiting in the foreground (exitcode=0): ./trc.d/sync.2
57 | 2017-02-23 01:04:18 trc [main/7207]: Going down. Running shutdown scripts...
58 | 2017-02-23 01:04:18 trc [halt/7256]: Running the shutdown script: ./trc.d/halt.delfile
59 | 2017-02-23 01:04:18 trc [main/7207]: Handling of termination...
60 | 2017-02-23 01:04:18 trc [main/7207]: Exited.
61 | ```
62 |
--------------------------------------------------------------------------------
/examples/config-files/trc.d/async.info:
--------------------------------------------------------------------------------
1 | if ! touch ${INFO_FILE}; then
2 | print "Cannot create file ${INFO_FILE}"
3 | exit 1
4 | fi
5 | print "host:\t${MYHOSTNAME}\nip:\t${MYIP}\nlogin:\t${USERNAME}\n" > ${INFO_FILE}
6 |
--------------------------------------------------------------------------------
/examples/config-files/trc.d/boot.funcs:
--------------------------------------------------------------------------------
1 | print() {
2 | echo -en "$@"
3 | }
4 | declare -fx print
5 |
--------------------------------------------------------------------------------
/examples/config-files/trc.d/boot.setvars:
--------------------------------------------------------------------------------
1 | export MYIP=$(hostname -i)
2 | export MYHOSTNAME=$(hostname)
3 | export USERNAME=$(id -un)
4 | export WAIT_TIME=1
5 | export INFO_FILE="localinfo.log"
6 |
--------------------------------------------------------------------------------
/examples/config-files/trc.d/halt.delfile:
--------------------------------------------------------------------------------
1 | [[ -f "${INFO_FILE}" ]] && rm -f ${INFO_FILE} || true
2 |
--------------------------------------------------------------------------------
/examples/config-files/trc.d/sync.1:
--------------------------------------------------------------------------------
1 | sleep ${WAIT_TIME}
2 |
--------------------------------------------------------------------------------
/examples/config-files/trc.d/sync.2:
--------------------------------------------------------------------------------
1 | [[ -f "${INFO_FILE}" ]] && cat ${INFO_FILE} || true
2 |
--------------------------------------------------------------------------------
/examples/docker-base-images/Dockerfile.alpine:
--------------------------------------------------------------------------------
1 | FROM alpine:latest
2 |
3 | RUN apk add --no-cache bash procps
4 |
5 | RUN wget -qP /etc/ http://trivialrc.cf/trc && \
6 | ( cd /etc && wget -qO - http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \
7 | chmod +x /etc/trc && \
8 | /etc/trc --version
9 |
10 | ENTRYPOINT ["/etc/trc"]
11 |
--------------------------------------------------------------------------------
/examples/docker-base-images/Dockerfile.centos:
--------------------------------------------------------------------------------
1 | FROM centos:latest
2 |
3 | RUN curl -sSLfo /etc/trc /etc/trc http://trivialrc.cf/trc && \
4 | ( cd /etc && curl -sSLf /etc/trc http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \
5 | chmod +x /etc/trc && \
6 | /etc/trc --version
7 |
8 | ENTRYPOINT ["/etc/trc"]
9 |
--------------------------------------------------------------------------------
/examples/docker-base-images/README.md:
--------------------------------------------------------------------------------
1 | # The example of building docker base images with TrivialRC as an ENTRYPOINT
2 |
3 | In this example, as base images were taken CentOS and Alpine.
4 | Basically, it shows two different approaches which are specific for each distribution.
5 |
6 | ## Build
7 |
8 | Run these commands from the directory with the example
9 |
10 | ```bash
11 | $ docker build -t centos-base -f Dockerfile.centos .
12 | $ docker build -t alpine-base -f Dockerfile.alpine .
13 | ```
14 |
15 | ## Test
16 |
17 | Let's test that TrivialRC works as expected
18 |
19 | ```bash
20 | $ docker run --rm -e RC_VERBOSE=true centos-base
21 | 2017-03-05 22:31:15 trc [main/1]: The wait policy: wait_any
22 | 2017-03-05 22:31:15 trc [main/1]: Trying to terminate sub-processes...
23 | 2017-03-05 22:31:15 trc [main/1]: Exited (exitcode=0)
24 |
25 | $ docker run --rm -e RC_VERBOSE=true alpine-base
26 | 2017-03-05 22:31:29 trc [main/1]: The wait policy: wait_any
27 | 2017-03-05 22:31:29 trc [main/1]: Trying to terminate sub-processes...
28 | 2017-03-05 22:31:29 trc [main/1]: Exited (exitcode=0)
29 | ```
30 |
--------------------------------------------------------------------------------
/examples/docker-config-templates/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM haproxy:1.7-alpine
2 |
3 | # Install TrivialRC
4 | RUN apk add --no-cache bash procps
5 | RUN wget -qP /etc/ http://trivialrc.cf/trc && \
6 | ( cd /etc && wget -qO - http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \
7 | chmod +x /etc/trc && \
8 | /etc/trc --version
9 | COPY trc.d/ /etc/trc.d/
10 |
11 | # Install FakeTpl
12 | RUN wget -qP /usr/bin/ http://faketpl.vorakl.name/faketpl && \
13 | ( cd /usr/bin && wget -qO - http://faketpl.vorakl.name/faketpl.sha256 | sha256sum -c )
14 |
15 | # Add a template. The config file will be made at run-time
16 | COPY haproxy.cfg.ftpl /usr/local/etc/haproxy/
17 |
18 | ENTRYPOINT ["/etc/trc"]
19 |
--------------------------------------------------------------------------------
/examples/docker-config-templates/README.md:
--------------------------------------------------------------------------------
1 | # Changing configuration of HAProxy in a docker container at run-time
2 |
3 | In the example, for transforming templates to configuration files is used [FakeTpl](https://github.com/vorakl/FakeTpl).
4 | To understand how it works it's worth following the link and read its documentation.
5 |
6 | ## Build
7 |
8 | Run this command from the directory with the example
9 |
10 | ```bash
11 | docker build -t trc-test-conftpl .
12 | ```
13 |
14 | ## Environment
15 |
16 | To test HAProxy by balancing some traffic, there will be used three instances with Nginx + uWSGI + Python Flask application which was explained in the another example - [Two applications in one docker image](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-two-apps). They all will be started from the same docker image on three different IP addresses and port 80.
17 |
18 | Let's create three additional IP addresses on the localhost for future instances:
19 |
20 | ```bash
21 | $ sudo ip addr add 127.0.0.2/32 dev lo
22 | $ sudo ip addr add 127.0.0.3/32 dev lo
23 | $ sudo ip addr add 127.0.0.4/32 dev lo
24 | ```
25 |
26 | And run three containers:
27 |
28 | ```bash
29 | $ docker run -d -p 127.0.0.2:80:80 --name trc-test-2apps-1 -e RC_VERBOSE=true trc-test-2apps
30 | $ docker run -d -p 127.0.0.3:80:80 --name trc-test-2apps-2 -e RC_VERBOSE=true trc-test-2apps
31 | $ docker run -d -p 127.0.0.4:80:80 --name trc-test-2apps-3 -e RC_VERBOSE=true trc-test-2apps
32 | ```
33 |
34 | So, now there are 3 instances with the same web application and they all are answering on the port 80 but on different IPs.
35 | Take a look at the `host` field, it will be needed later for finding out that balancing works:
36 |
37 | ```bash
38 | $ curl http://127.0.0.2/
39 | {
40 | "base_url": "http://127.0.0.2/",
41 | "cookies": {},
42 | "full_path": "/?",
43 | "headers": {
44 | "Accept": "*/*",
45 | "Content-Length": "",
46 | "Content-Type": "",
47 | "Host": "127.0.0.2",
48 | "User-Agent": "curl/7.51.0"
49 | },
50 | "host": "2254f0879ac2",
51 | "host_url": "http://127.0.0.2/",
52 | "method": "GET",
53 | "path": "/",
54 | "query_string": "",
55 | "scheme": "http"
56 | }
57 |
58 | $ curl http://127.0.0.3/
59 | {
60 | "base_url": "http://127.0.0.3/",
61 | "cookies": {},
62 | "full_path": "/?",
63 | "headers": {
64 | "Accept": "*/*",
65 | "Content-Length": "",
66 | "Content-Type": "",
67 | "Host": "127.0.0.3",
68 | "User-Agent": "curl/7.51.0"
69 | },
70 | "host": "e758ded171f6",
71 | "host_url": "http://127.0.0.3/",
72 | "method": "GET",
73 | "path": "/",
74 | "query_string": "",
75 | "scheme": "http"
76 | }
77 |
78 | $ curl http://127.0.0.4
79 | {
80 | "base_url": "http://127.0.0.4/",
81 | "cookies": {},
82 | "full_path": "/?",
83 | "headers": {
84 | "Accept": "*/*",
85 | "Content-Length": "",
86 | "Content-Type": "",
87 | "Host": "127.0.0.4",
88 | "User-Agent": "curl/7.51.0"
89 | },
90 | "host": "50afa4eba886",
91 | "host_url": "http://127.0.0.4/",
92 | "method": "GET",
93 | "path": "/",
94 | "query_string": "",
95 | "scheme": "http"
96 | }
97 | ```
98 |
99 | ## Preparation
100 |
101 | The last piece of a puzzle - load balancer.
102 | A docker image with HAProxy has only a template of its configuration.
103 | You can modify a final configuration of HAProxy by changing these environment variables.
104 | They all, except BACKEND, have have default values.
105 |
106 | * *BACKEND* (array) []
107 | * *BIND_IP* (scalar) [127.0.0.1]
108 | * *MAXCONN* (scalar) [2000]
109 | * *TIMEOUT_CONNECT* (scalar) [5000]
110 | * *TIMEOUT_CLIENT* (scalar) [10000]
111 | * *TIMEOUT_SERVER* (scalar) [10000]
112 |
113 | This HAProxy is gonna use host's network to simplify things. The list of backend's names and addresses should be put in the BACKEND array variable. All logs could be found in the host's systemd journal.
114 |
115 | ## Test
116 |
117 | It's time to run a container with HAProxy
118 |
119 | ```bash
120 | $ docker run -d --name trc-test-conftpl -e RC_VERBOSE=true -e BACKEND="([web1]=127.0.0.2 [web2]=127.0.0.3 [web3]=127.0.0.4)" --net=host -v /run/systemd/journal/:/host-journal trc-test-conftpl
121 |
122 | $ docker logs -f trc-test-conftpl
123 | 2017-03-08 18:56:48 trc [main/1]: The wait policy: wait_any
124 | 2017-03-08 18:56:48 trc [main/1]: Launching in the boot: /etc/trc.d/boot.make-conf
125 | 2017-03-08 18:56:48 trc [main/1]: Building a new config file from the template:
126 | global
127 | log /host-journal/dev-log local0
128 | maxconn 2000
129 | stats socket /tmp/haproxy.sock
130 | defaults
131 | log global
132 | mode http
133 | option httplog
134 | option dontlognull
135 | retries 3
136 | option redispatch
137 | option forwardfor
138 | timeout connect 5000
139 | timeout client 10000
140 | timeout server 10000
141 | frontend web
142 | bind 127.0.0.1:80
143 | default_backend web_dyn
144 | backend web_dyn
145 | balance roundrobin
146 | server web1 127.0.0.2:80 check
147 | server web2 127.0.0.3:80 check
148 | server web3 127.0.0.4:80 check
149 | 2017-03-08 18:56:48 trc [async/30]: Launching in the background: /etc/trc.d/async.haproxy
150 | <7>haproxy-systemd-wrapper: executing /usr/local/sbin/haproxy -p /run/haproxy.pid -f /usr/local/etc/haproxy/haproxy.cfg -Ds
151 | ```
152 |
153 | Everything is Up and Running!
154 | We can access localhost port 80 three times in row and get the answer from three different containers:
155 |
156 | ```bash
157 | $ curl -s http://127.0.0.1/ | grep '"host"'
158 | "host": "2254f0879ac2",
159 |
160 | $ curl -s http://127.0.0.1/ | grep '"host"'
161 | "host": "e758ded171f6",
162 |
163 | $ curl -s http://127.0.0.1/ | grep '"host"'
164 | "host": "50afa4eba886",
165 | ```
166 |
167 | All logs from HAProxy is being sent to the host's Journal and can be easily accessed by filtering a sarvice name:
168 |
169 | ```bash
170 | $ sudo journalctl -b -f _COMM=haproxy
171 | Mar 08 19:59:02 marche haproxy[11492]: 127.0.0.1:58086 [08/Mar/2017:18:59:02.514] web web_dyn/web1 0/0/0/2/2 200 566 - - ---- 1/1/0/1/0 0/0 "GET / HTTP/1.1"
172 | Mar 08 19:59:06 marche haproxy[11492]: 127.0.0.1:58102 [08/Mar/2017:18:59:06.123] web web_dyn/web2 0/0/0/2/2 200 566 - - ---- 1/1/0/1/0 0/0 "GET / HTTP/1.1"
173 | Mar 08 19:59:09 marche haproxy[11492]: 127.0.0.1:58118 [08/Mar/2017:18:59:09.474] web web_dyn/web3 0/0/0/2/2 200 566 - - ---- 1/1/0/1/0 0/0 "GET / HTTP/1.1"
174 | ```
175 |
176 | Let's remove all these containers and IP addresses:
177 |
178 | ```bash
179 | $ docker rm -f trc-test-conftpl trc-test-2apps-1 trc-test-2apps-2 trc-test-2apps-3
180 | trc-test-conftpl
181 | trc-test-2apps-1
182 | trc-test-2apps-2
183 | trc-test-2apps-3
184 |
185 | $ sudo ip addr del 127.0.0.2/32 dev lo
186 | $ sudo ip addr del 127.0.0.3/32 dev lo
187 | $ sudo ip addr del 127.0.0.4/32 dev lo
188 | ```
189 |
190 |
--------------------------------------------------------------------------------
/examples/docker-config-templates/haproxy.cfg.ftpl:
--------------------------------------------------------------------------------
1 | global
2 | log /host-journal/dev-log local0
3 | maxconn ${MAXCONN:-2000}
4 | stats socket /tmp/haproxy.sock
5 |
6 | defaults
7 | log global
8 | mode http
9 | option httplog
10 | option dontlognull
11 | retries 3
12 | option redispatch
13 | option forwardfor
14 | timeout connect ${TIMEOUT_CONNECT:-5000}
15 | timeout client ${TIMEOUT_CLIENT:-10000}
16 | timeout server ${TIMEOUT_SERVER:-10000}
17 |
18 | frontend web
19 | bind ${BIND_IP:-127.0.0.1}:80
20 | default_backend web_dyn
21 |
22 | backend web_dyn
23 | balance ${LB_ALG:-roundrobin}
24 | $(IFS=' '; for host in $(tr ' ' '\n' <<< ${!BACKENDS[@]} | sort -n | tr '\n' ' '); do echo " server ${host} ${BACKENDS[${host}]}:80 check"; done)
25 |
26 |
--------------------------------------------------------------------------------
/examples/docker-config-templates/trc.d/async.haproxy:
--------------------------------------------------------------------------------
1 | /usr/local/sbin/haproxy-systemd-wrapper -p /run/haproxy.pid -f /usr/local/etc/haproxy/haproxy.cfg
2 |
--------------------------------------------------------------------------------
/examples/docker-config-templates/trc.d/boot.make-conf:
--------------------------------------------------------------------------------
1 | # import faketpl function
2 | . faketpl
3 |
4 | # It takes scalar from environment and makes array from it
5 | if [[ -n "${BACKEND}" ]]; then
6 | declare -A BACKENDS
7 | eval BACKENDS=${BACKEND};
8 | fi
9 |
10 | log "Building a new config file from the template:"
11 | (faketpl < /usr/local/etc/haproxy/haproxy.cfg.ftpl | tee /usr/local/etc/haproxy/haproxy.cfg)
12 |
--------------------------------------------------------------------------------
/examples/docker-service-discovery/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM centos
2 | MAINTAINER "Oleksii Tsvietnov"
3 |
4 | ENV RC_VERBOSE true
5 | ENV ZKURL 127.0.0.1
6 |
7 | RUN curl -sSLfo /etc/trc http://trivialrc.cf/trc && \
8 | ( cd /etc && wget -qO - http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \
9 | chmod +x /etc/trc && \
10 | /etc/trc --version
11 |
12 | RUN curl -sSLfo /usr/bin/zookeepercli https://github.com/outbrain/zookeepercli/releases/download/v1.0.10/zookeepercli && \
13 | chmod +x /usr/bin/zookeepercli
14 |
15 | COPY trc.d/ /etc/trc.d/
16 |
17 | ENTRYPOINT ["/etc/trc"]
18 |
--------------------------------------------------------------------------------
/examples/docker-service-discovery/README.md:
--------------------------------------------------------------------------------
1 | # The example of a registration a newly created docker container in the Service Discovery
2 |
3 | In this example, after a docker container has started up,
4 | it's going to register in the Service Discovery as a first step.
5 | After that, it will start the main application and, in the end, on exit by any reasons
6 | the container will remove itself from the Service Discovery as the last step.
7 |
8 | As a Service Discovery is used ZooKeeper and you can specify the URL during the docker run.
9 |
10 | ## Build
11 |
12 | Run this command from the directory with the example
13 |
14 | ```bash
15 | docker build -t trc-test-sd .
16 | ```
17 |
18 | ## Test
19 |
20 | To test this example you need an IP of a working ZooKeeper cluster.
21 | The 'virtual app' can be interrupted by pressing ``.
22 |
23 | The command line will look like
24 |
25 | ```bash
26 | $ docker run --rm -it -e ZKURL="srv1[:port1][,srv2[:port2]...]" trc-test-sd
27 | ```
28 | For example:
29 |
30 | ```bash
31 | $ docker run --rm -it -e ZKURL=192.168.1.173 trc-test-sd
32 | 2017-03-03 16:49:06 trc [main/1]: The wait policy: wait_any
33 | 2017-03-03 16:49:06 trc [main/1]: Launching in the boot: /etc/trc.d/boot.sd-reg
34 | 2017-03-03 16:49:06 trc [main/1]: 2a3556e6f646 has been registered at 192.168.1.173
35 | 2017-03-03 16:49:06 trc [sync/29]: Launching in the foreground: /etc/trc.d/sync.app
36 | I am alive! Press to exit...
37 | I am alive! Press to exit...
38 | I am alive! Press to exit...
39 | ^C2017-03-03 16:49:08 trc [sync/29]: Exiting in the foreground (exitcode=130): /etc/trc.d/sync.app
40 | 2017-03-03 16:49:09 trc [main/1]: Trying to terminate sub-processes...
41 | 2017-03-03 16:49:09 trc [halt/44]: Running the shutdown script: /etc/trc.d/halt.sd-unreg
42 | 2017-03-03 16:49:09 trc [halt/44]: 2a3556e6f646 has been unregistered at 192.168.1.173
43 | 2017-03-03 16:49:09 trc [main/1]: Exiting from the shutdown script (exitcode=0): /etc/trc.d/halt.sd-unreg
44 | 2017-03-03 16:49:09 trc [main/1]: Exited.
45 |
46 | $ echo $?
47 | 0
48 | ```
49 |
--------------------------------------------------------------------------------
/examples/docker-service-discovery/trc.d/boot.sd-reg:
--------------------------------------------------------------------------------
1 | if ! zookeepercli -servers "${ZKURL}" -c get /nodes &>/dev/null
2 | then
3 | zookeepercli -servers "${ZKURL}" -c create /nodes "" &>/dev/null || err "Cannot connect to the ZooKeeper at ${ZKURL}"
4 | fi
5 |
6 | zookeepercli -servers "${ZKURL}" -c create /nodes/$(hostname) ""
7 | log "$(hostname) has been registered at ${ZKURL}"
8 |
--------------------------------------------------------------------------------
/examples/docker-service-discovery/trc.d/halt.sd-unreg:
--------------------------------------------------------------------------------
1 | # Ignore any errors on exit
2 | set +e
3 |
4 | if zookeepercli -servers "${ZKURL}" -c get /nodes/$(hostname) &>/dev/null
5 | then
6 | zookeepercli -servers "${ZKURL}" -c rm /nodes/$(hostname)
7 | log "$(hostname) has been unregistered at ${ZKURL}"
8 | fi
9 |
10 | # Ignoring exit status 130 (after pressing Ctrl+C) by finishing with status 0
11 | exit 0
12 |
--------------------------------------------------------------------------------
/examples/docker-service-discovery/trc.d/sync.app:
--------------------------------------------------------------------------------
1 | while true; do
2 | echo "I am alive! Press to exit..."
3 | sleep 1
4 | done
5 |
--------------------------------------------------------------------------------
/examples/docker-two-apps/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:edge
2 | MAINTAINER "Oleksii Tsvietnov"
3 |
4 | RUN apk add --no-cache nginx uwsgi uwsgi-python3 && \
5 | pip3 install --upgrade pip && \
6 | pip3 install flask
7 |
8 | RUN mkdir /run/uwsgi && \
9 | chown nginx:nginx /run/uwsgi
10 | COPY app/ /app/
11 |
12 | # The required packages for TrivialRC to be run on Alpine Linux
13 | RUN apk add --no-cache bash procps
14 | RUN wget -qP /etc/ http://trivialrc.cf/trc && \
15 | ( cd /etc && wget -qO - http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \
16 | chmod +x /etc/trc && \
17 | /etc/trc --version
18 | COPY etc/ /etc/
19 |
20 | EXPOSE 80
21 |
22 | ENTRYPOINT ["/etc/trc"]
23 |
--------------------------------------------------------------------------------
/examples/docker-two-apps/README.md:
--------------------------------------------------------------------------------
1 | # The example of launching two applications within one docker container
2 |
3 | In this example we will launch Python Flask application plus uWSGI behind Nginx in one docker container.
4 | They will work simultaneously and communicate to each other inside the same container.
5 | If any of these serviceses, either uWSGI or Nginx, at any point of time, has stopped working by some reasons,
6 | the whole container will be stopped as well by TrivialRC.
7 |
8 | ## Build
9 |
10 | Run this command from the directory with the example:
11 |
12 | ```bash
13 | docker build -t trc-test-2apps .
14 | ```
15 |
16 | ## Test
17 |
18 | Let's run a new container with mapping its port 80 to the host's port 8080:
19 |
20 | ```bash
21 | docker run -d -p 8080:80 --name trc-test-2apps -e RC_VERBOSE=true trc-test-2apps
22 | ```
23 |
24 | To check if both services were started and a container is up and running, run next commands:
25 |
26 | ```bash
27 | docker ps -al
28 | docker logs trc-test-2apps
29 | ```
30 |
31 | Now we can check if the Flask application is answering by accessing port 8080 on the localhost.
32 | It should show a JSON document with HTTP request headers.
33 |
34 | ```bash
35 | $ curl http://localhost:8080/
36 | {
37 | "base_url": "http://localhost:8080/",
38 | "cookies": {},
39 | "full_path": "/?",
40 | "headers": {
41 | "Accept": "*/*",
42 | "Content-Length": "",
43 | "Content-Type": "",
44 | "Host": "localhost:8080",
45 | "User-Agent": "curl/7.51.0"
46 | },
47 | "host": "52d0ba70fef2",
48 | "host_url": "http://localhost:8080/",
49 | "method": "GET",
50 | "path": "/",
51 | "query_string": "",
52 | "scheme": "http"
53 | }
54 | ```
55 |
56 | If you wanna check that a container is really being stopped when uWSGI or Nginx doesn't work in the container,
57 | you can stop any of them while a container is still running and check its status:
58 |
59 | ```bash
60 | $ docker exec trc-test-2apps pkill nginx
61 |
62 | $ docker ps -al
63 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
64 | 52d0ba70fef2 trc-test-2apps "/etc/trc" 12 minutes ago Exited (143) 8 seconds ago trc-test-2apps
65 |
66 | $ docker logs trc-test-2apps |& tail -10
67 | WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x559785bca080 pid: 26 (default app)
68 | *** uWSGI is running in multiple interpreter mode ***
69 | spawned uWSGI worker 1 (and the only) (pid: 26, cores: 16)
70 | [pid: 26|app: 0|req: 1/1] 172.17.0.1 () {32 vars in 324 bytes} [Sat Mar 4 21:26:15 2017] GET / => generated 389 bytes in 4 msecs (HTTP/1.1 200) 2 headers in 72 bytes (2 switches on core 1)
71 | [04/Mar/2017:21:26:15 +0000] localhost 172.17.0.1 "GET / HTTP/1.1" 200 389 "-" "curl/7.51.0" "-"
72 | 2017-03-04 21:33:31 trc [async/16]: Exiting in the background (exitcode=0): /etc/trc.d/async.nginx
73 | 2017-03-04 21:33:31 trc [main/1]: Trying to terminate sub-processes...
74 | 2017-03-04 21:33:31 trc [main/1]: terminating the child process
75 | 2017-03-04 21:33:31 trc [async/17]: Exiting in the background (exitcode=30): /etc/trc.d/async.uwsgi
76 | 2017-03-04 21:33:32 trc [main/1]: Exited (exitcode=143)
77 | ```
78 |
79 | And don't forget to remove a container manually:)
80 |
81 | ```bash
82 | $ docker rm trc-test-2apps
83 | ```
84 |
--------------------------------------------------------------------------------
/examples/docker-two-apps/app/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | from flask import request
3 | from flask import jsonify
4 | import socket
5 |
6 | app = Flask(__name__)
7 |
8 | @app.route("/")
9 | def headers():
10 | return jsonify(host=socket.gethostname(),
11 | host_url=request.host_url,
12 | base_url=request.base_url,
13 | headers={ k:v for k, v in request.headers },
14 | cookies=request.cookies,
15 | query_string=request.query_string.decode(),
16 | path=request.path,
17 | full_path=request.full_path,
18 | method=request.method,
19 | scheme=request.scheme)
20 |
21 | if __name__ == "__main__":
22 | app.run()
23 |
--------------------------------------------------------------------------------
/examples/docker-two-apps/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/examples/docker-two-apps/app/favicon.ico
--------------------------------------------------------------------------------
/examples/docker-two-apps/etc/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | worker_processes auto;
2 | daemon off;
3 | pid /run/nginx.pid;
4 | error_log /dev/stderr warn;
5 | user nginx;
6 |
7 |
8 | events {
9 | worker_connections 512;
10 | }
11 |
12 | http {
13 | include mime.types;
14 | sendfile on;
15 | keepalive_timeout 70;
16 |
17 | log_format custom '[$time_local] $host $remote_addr "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$gzip_ratio"';
18 | access_log /dev/stdout custom;
19 |
20 | server {
21 | listen 80 default_server;
22 | server_name _;
23 | root /app;
24 |
25 | location / {
26 | try_files $uri @flaskApp;
27 | }
28 |
29 | location @flaskApp {
30 | include uwsgi_params;
31 | uwsgi_pass unix:/run/uwsgi/uwsgiApp.sock;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/examples/docker-two-apps/etc/trc.d/async.nginx:
--------------------------------------------------------------------------------
1 | nginx
2 |
--------------------------------------------------------------------------------
/examples/docker-two-apps/etc/trc.d/async.uwsgi:
--------------------------------------------------------------------------------
1 | uwsgi --ini /etc/uwsgi.ini
2 |
--------------------------------------------------------------------------------
/examples/docker-two-apps/etc/uwsgi.ini:
--------------------------------------------------------------------------------
1 | [uwsgi]
2 | plugins = /usr/lib/uwsgi/python3
3 | chdir = /app
4 | module = app:app
5 | uid = nginx
6 | gid = nginx
7 | socket = /run/uwsgi/uwsgiApp.sock
8 | pidfile = /run/uwsgi/uwsgi.pid
9 | processes = 1
10 | threads = 16
11 |
--------------------------------------------------------------------------------
/examples/one-liners/README.md:
--------------------------------------------------------------------------------
1 | # One Liners
2 |
3 | ```bash
4 | # The simplest start is doing nothing
5 | $ ./trc
6 | ```
7 | ```bash
8 | # The most famous one
9 | $ ./trc echo Hello World
10 | Hello World
11 | ```
12 | ```bash
13 | # Turns on logs
14 | $ RC_VERBOSE=true ./trc echo Hello World
15 | 2017-02-22 23:10:11 trc [main/23630]: The wait policy: wait_any
16 | 2017-02-22 23:10:11 trc [sync/23640]: Launching in the foreground: echo Hello World
17 | Hello World
18 | 2017-02-22 23:10:11 trc [sync/23640]: Exiting in the foreground (exitcode=0): echo Hello World
19 | 2017-02-22 23:10:11 trc [main/23630]: Going down. Running shutdown scripts...
20 | 2017-02-22 23:10:11 trc [main/23630]: Handling of termination...
21 | 2017-02-22 23:10:11 trc [main/23630]: Exited.
22 |
23 | $ echo $?
24 | 0
25 | ```
26 | ```bash
27 | # Exits with a proper exit code
28 | $ ./trc exit 111
29 |
30 | $ echo $?
31 | 111
32 | ```
33 | ```bash
34 | # Both commands are running in the foreground but it exits after the first one by default
35 | $ RC_VERBOSE=true \
36 | RC_VERBOSE_EXTRA=true \
37 | ./trc -F 'echo Hello'
38 | echo World
39 | 2017-02-22 23:14:35 trc [main/24314]: The wait policy: wait_any
40 | 2017-02-22 23:14:35 trc [sync/24324]: Launching in the foreground: echo Hello
41 | Hello
42 | 2017-02-22 23:14:35 trc [sync/24324]: - exit-trap (exitcode=0)
43 | 2017-02-22 23:14:35 trc [sync/24324]: Exiting in the foreground (exitcode=0): echo Hello
44 | 2017-02-22 23:14:35 trc [sync/24324]: - terminating the main process
45 | 2017-02-22 23:14:35 trc [main/24314]: - sig-trap (exitcode=0)
46 | 2017-02-22 23:14:35 trc [main/24314]: - exit-trap (exitcode=0)
47 | 2017-02-22 23:14:35 trc [main/24314]: Going down. Running shutdown scripts...
48 | 2017-02-22 23:14:35 trc [main/24314]: Handling of termination...
49 | 2017-02-22 23:14:35 trc [main/24314]: Exited.
50 | ```
51 | ```bash
52 | # To achive the same goal it needs to wait for all commands and then we'll see both outputs
53 | $ RC_WAIT_POLICY=wait_all ./trc -F 'echo Hello' echo World
54 | Hello
55 | World
56 | ```
57 | ```bash
58 | # A few ways to run many commands in the foreground
59 | $ RC_WAIT_POLICY=wait_all \
60 | ./trc -F 'echo Hello' \
61 | -F 'sleep 1' \
62 | -F 'echo World'
63 | Hello
64 | World
65 |
66 | $ ./trc -F 'echo Hello; sleep 1; echo World'
67 | Hello
68 | World
69 | ```
70 | ```bash
71 | # It's going to create file in the background, waiting for 3 sec and then reading this file
72 | $ RC_WAIT_POLICY=wait_all \
73 | ./trc -D 'date > date1.log' \
74 | -F 'sleep 3' \
75 | -F 'echo -e "Old time: $(cat date1.log)\nNew time: $(date)"'
76 | -F 'rm -f date1.log'
77 | Old time: Wed Feb 22 14:15:20 CET 2017
78 | New time: Wed Feb 22 14:15:23 CET 2017
79 |
80 | $ ls -l date1.log
81 | ls: cannot access 'date1.log': No such file or directory
82 | ```
83 | ```bash
84 | # It assigns environment variables in the boot-block an then uses them in the foreground-block
85 | ./trc -B 'export myhost=$(hostname) user=$(id -un)' \
86 | -F 'echo -e "Username: $user\nHostname: $myhost"'
87 | Username: vorakl
88 | Hostname: marche
89 | ```
90 | ```bash
91 | # It catches all exit codes of all background processes and prints them out in a readable way
92 | $ RC_VERBOSE=true \
93 | RC_WAIT_POLICY=wait_all \
94 | ./trc -D 'exit 2' \
95 | -D 'false' \
96 | -D 'exit 4' \
97 | -D 'true' | \
98 | sed -n 's|^.*(exitcode=\([[:digit:]]*\)): \(.*\)$|\2\t : \1|p'
99 | exit 2 : 2
100 | true : 0
101 | false : 1
102 | exit 4 : 4
103 | ```
104 | ```bash
105 | # It catches an exit code of the first failed command.
106 | # It's `exit 4` because `exit 0` didn't fail and all other commands stil run
107 | # For this example it needs a `wait_err` wait policy
108 | $ RC_WAIT_POLICY=wait_err ./trc -D 'sleep 1; exit 2' -D 'sleep 2; exit 3' -F 'exit 0' -F 'exit 4' exit 5
109 |
110 | $ echo $?
111 | 4
112 |
113 | # And now, let's run the same example but with a `wait_all` wait policy.
114 | # In this case, it will wait for all commands and exit with a status of the last command which is `sleep 2; exit 3`
115 | $ RC_WAIT_POLICY=wait_all ./trc -D 'sleep 1; exit 2' -D 'sleep 2; exit 3' -F 'exit 0' -F 'exit 4' exit 5
116 |
117 | $ echo $?
118 | 3
119 | ```
120 |
121 |
--------------------------------------------------------------------------------
/examples/process-manager/README.md:
--------------------------------------------------------------------------------
1 | # Managing a group of processes
2 |
3 | When your application is quite complex and it relies on a specific group of processes (other applications) there is a need to having a controller of this group as a whole. Usually, all processes have to be in "Up and Running" state all the time for a proper functioning. For this purpose, a process manager restarts some of them in case of failures, writes some messages to the log, etc. If it's not possible to bring back to life a failed app, it should puts down the whole group, notify about this situation and finishes with a proper exit status.
4 |
5 | So, that's the solution! It consists of systemd unit that runs TrivialRC that runs, let's say, some three apps. Each of them will be run asynchronously. There is a special routine in each async task that restarts the app if it fails and writes a message about that to the main log using one of [internal functions](https://github.com/vorakl/TrivialRC#integrated-functions). There are available a few function for logging which are printing out messages in the same format as TrivialRC does and depending on values of environment variables (RC_VERBOSE, RC_VERBOSE_EXTRA). Please, follow the link for more information.
6 |
7 | To prevent printing out stdout/stderr from all applications to the same log at the same time by making a real mess, each async task will redirect stdout/stderr to the uniq named pipe per task and then another async task will be printing them out, line by line, and prefixing lines by a task id. All uniq pipes for logging will be created at `boot` stage and removed at `halt` stage. By doing this, we will have in the main log only TrivialRC's logs regarding launching tasks and logs from tasks if they were sent by `log` of `warn` functions.
8 | All these messages will be seen via `journalctl`.
9 |
10 | ## Installation
11 |
12 | To try this example you need to acomplish 3 steps:
13 |
14 | * Install TrivialRC script in a standart path as `/usr/bin/trc` and give it an execute permission
15 | * Copy configuration into /opt/app, so you've got `/opt/app/trc.d/` with all config files
16 | * Copy Systemd unit `trc-example-procmgr.service` to /etc/systemd/system/ and run `sudo systemctl enable trc-example-procmgr`
17 |
18 | ## Test
19 |
20 | If these steps are done, you can simply start Systemd unit and check via journalctl how it reacts on killing different processes from the group:
21 |
22 | ```bash
23 | $ sudo systemctl start trc-example-procmgr
24 | $ sudo kill `pidof ss`
25 | $ sudo kill `pidof nc`
26 | $ sudo systemctl stop trc-example-procmgr
27 |
28 | $ sudo journalctl -o cat -u trc-example-procmgr
29 | Started The example of running TrivialRC as a process manager.
30 | 2017-03-06 23:46:29 trc [main/29871]: The wait policy: wait_any
31 | 2017-03-06 23:46:29 trc [main/29871]: Launching in the boot: /opt/app/trc.d/boot.create-logs
32 | 2017-03-06 23:46:29 trc [async/29929]: Launching in the background: /opt/app/trc.d/async.netlistener-logger
33 | 2017-03-06 23:46:29 trc [async/29933]: Launching in the background: /opt/app/trc.d/async.netmon-logger
34 | 2017-03-06 23:46:29 trc [async/29928]: Launching in the background: /opt/app/trc.d/async.netlistener
35 | 2017-03-06 23:46:29 trc [async/29931]: Launching in the background: /opt/app/trc.d/async.netmon
36 | 2017-03-06 23:46:29 trc [async/29934]: Launching in the background: /opt/app/trc.d/async.netsender
37 | 2017-03-06 23:46:29 trc [async/29931]: Start monitoring TCP activity...
38 | netmon: State Recv-Q Send-Q Local Address:Port Peer Address:Port
39 | 2017-03-06 23:46:29 trc [async/29928]: Start a network listener on the port 1234...
40 | netlistener: Mon Mar 6 23:46:29 CET 2017
41 | netlistener: Mon Mar 6 23:46:30 CET 2017
42 | netlistener: Mon Mar 6 23:46:31 CET 2017
43 | netlistener: Mon Mar 6 23:46:32 CET 2017
44 | netlistener: Mon Mar 6 23:46:33 CET 2017
45 | netlistener: Mon Mar 6 23:46:34 CET 2017
46 | Terminated
47 | 017-03-06 23:46:34 trc [async/29931]: Monitoring tool has stopped working!
48 | netlistener: Mon Mar 6 23:46:35 CET 2017
49 | 2017-03-06 23:46:35 trc [async/29931]: Start monitoring TCP activity...
50 | netmon: State Recv-Q Send-Q Local Address:Port Peer Address:Port
51 | netlistener: Mon Mar 6 23:46:36 CET 2017
52 | netlistener: Mon Mar 6 23:46:37 CET 2017
53 | Terminated
54 | 2017-03-06 23:46:37 trc [async/29928]: The network listener has stopped working!
55 | 2017-03-06 23:46:38 trc [async/29928]: Start a network listener on the port 1234...
56 | netlistener: Mon Mar 6 23:46:39 CET 2017
57 | netlistener: Mon Mar 6 23:46:40 CET 2017
58 | netlistener: Mon Mar 6 23:46:41 CET 2017
59 | netlistener: Mon Mar 6 23:46:42 CET 2017
60 | netlistener: Mon Mar 6 23:46:43 CET 2017
61 | Stopping The example of running TrivialRC as a process manager...
62 | Terminated
63 | Terminated
64 | Terminated
65 | 2017-03-06 23:46:43 trc [main/29871]: Trying to terminate sub-processes...
66 | 2017-03-06 23:46:43 trc [async/29931]: Exiting in the background (exitcode=143): /opt/app/trc.d/async.netmon
67 | 2017-03-06 23:46:43 trc [async/29929]: Exiting in the background (exitcode=0): /opt/app/trc.d/async.netlistener-logger
68 | 2017-03-06 23:46:43 trc [async/29933]: Exiting in the background (exitcode=0): /opt/app/trc.d/async.netmon-logger
69 | 2017-03-06 23:46:43 trc [async/29934]: Exiting in the background (exitcode=143): /opt/app/trc.d/async.netsender
70 | 2017-03-06 23:46:43 trc [async/29928]: Exiting in the background (exitcode=143): /opt/app/trc.d/async.netlistener
71 | 2017-03-06 23:46:43 trc [main/29871]: terminating the child process
72 | 2017-03-06 23:46:43 trc [halt/30183]: Running the shutdown script: /opt/app/trc.d/halt.remove-logs
73 | 2017-03-06 23:46:43 trc [main/29871]: Exiting from the shutdown script (exitcode=143): /opt/app/trc.d/halt.remove-logs
74 | 2017-03-06 23:46:43 trc [main/29871]: Exited (exitcode=143)
75 | Stopped The example of running TrivialRC as a process manager.
76 | ```
77 |
78 | ## Conclusion
79 |
80 | You can notice that two processes were restarted automatically:
81 |
82 | ```bash
83 | 017-03-06 23:46:34 trc [async/29931]: Monitoring tool has stopped working!
84 | 2017-03-06 23:46:35 trc [async/29931]: Start monitoring TCP activity...
85 |
86 | 2017-03-06 23:46:37 trc [async/29928]: The network listener has stopped working!
87 | 2017-03-06 23:46:38 trc [async/29928]: Start a network listener on the port 1234...
88 | ```
89 | More over, every single line is prefixed by the task name as `netlistener` or `netmon`. This makes possible to filter the stream by the task
90 |
--------------------------------------------------------------------------------
/examples/process-manager/trc-example-procmgr.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=The example of running TrivialRC as a process manager
3 | After=network.target
4 |
5 | [Service]
6 | Type=simple
7 | Environment=RC_VERBOSE=true
8 | ExecStart=/usr/bin/trc --workdir /opt/app
9 | ExecStop=/bin/kill -TERM $MAINPID
10 | Restart=on-failure
11 | RestartSec=5s
12 | TimeoutStopSec=5s
13 | SuccessExitStatus=0 143 SIGTERM
14 |
15 | [Install]
16 | WantedBy=multi-user.target
17 |
--------------------------------------------------------------------------------
/examples/process-manager/trc.d/async.netlistener:
--------------------------------------------------------------------------------
1 | # Do not react on errors, keep working
2 | trap 'true' ERR
3 |
4 | while true; do
5 | log "Start a network listener on the port 1234..."
6 | nc -kl 1234 &> ${NETLISTENER}
7 | warn "The network listener has stopped working!"
8 | sleep 1
9 | done
10 |
--------------------------------------------------------------------------------
/examples/process-manager/trc.d/async.netlistener-logger:
--------------------------------------------------------------------------------
1 | # Do not react on errors, keep working
2 | trap 'true' ERR
3 | local _str
4 | exec 6<>${NETLISTENER}
5 |
6 | while read -u 6 _str; do
7 | echo "netlistener: ${_str}"
8 | done
9 |
--------------------------------------------------------------------------------
/examples/process-manager/trc.d/async.netmon:
--------------------------------------------------------------------------------
1 | # Do not react on errors, keep working
2 | trap 'true' ERR
3 |
4 | while true; do
5 | log "Start monitoring TCP activity..."
6 | ss -Etn &> ${NETMON}
7 | warn "Monitoring tool has stopped working!"
8 | sleep 1
9 | done
10 |
--------------------------------------------------------------------------------
/examples/process-manager/trc.d/async.netmon-logger:
--------------------------------------------------------------------------------
1 | # Do not react on errors, keep working
2 | trap 'true' ERR
3 | local _str
4 | exec 5<>${NETMON}
5 |
6 | while read -u 5 _str; do
7 | echo "netmon: ${_str}"
8 | done
9 |
--------------------------------------------------------------------------------
/examples/process-manager/trc.d/async.netsender:
--------------------------------------------------------------------------------
1 | # Do not react on errors, keep working
2 | trap 'true' ERR
3 |
4 | while true; do
5 | nc localhost 1234 <<< "$(date)" &>/dev/null
6 | sleep 1
7 | done
8 |
--------------------------------------------------------------------------------
/examples/process-manager/trc.d/boot.create-logs:
--------------------------------------------------------------------------------
1 | export NETMON="/tmp/netmon.pipe"
2 | export NETLISTENER="/tmp/netlistener.pipe"
3 | mkfifo ${NETMON}
4 | mkfifo ${NETLISTENER}
5 |
--------------------------------------------------------------------------------
/examples/process-manager/trc.d/halt.remove-logs:
--------------------------------------------------------------------------------
1 | # Do not fail on errors in halt stage
2 | set +e
3 |
4 | rm -f ${NETMON} ${NETLISTENER}
5 |
6 | # Finishing with the original exit status
7 | exit ${_exit_status}
8 |
--------------------------------------------------------------------------------
/examples/reliable-tests-for-docker-images/README.md:
--------------------------------------------------------------------------------
1 | # Reliable tests for docker images
2 |
3 | If you have automated builds of docker images, you need to be sure that these images are not only built successfuly but also run services. Sometimes, services fail immediately after they were started up in a container because of different reasons like segmentation fault, lack of some libraries because they were moved by an upstream package, wrong system permissions or something else that is hard to be predicted and cannot be noticed without actually running a service and testing that it is up and fully functional.
4 |
5 | This is an example of how services in containers can be reliably tested without any extra efforts by adding a test process in the background which runs all necessary tests and then, checking the exit status on exit.
6 |
7 | As an example docker image I'm going to use [OpenSMTPD](https://github.com/vorakl/docker-images/tree/master/centos-opensmtpd) service image. Then, inject a background test command. In case of a success test, it will send a specific signal to the main process and cause (because a default wait policy is `wait_any`) a stop the whole container. On exit, I'll inject a `halt` command to check if the main process has cought my signal (128 + 10 = 138). If yes, I'll rewrite an exit status to 0 (success). Otherwise, the script will finish with some other error.
8 |
9 | So many words but in fact it looks much more simple.
10 | The container with a service resides in [vorakl/centos-opensmtpd](https://hub.docker.com/r/vorakl/centos-opensmtpd/) and can be simply started as
11 |
12 | ```bash
13 | $ docker run -d --name smtpd --net host vorakl/centos-opensmtpd
14 | ```
15 |
16 | Let's add two additional commands to test the service and check the result:
17 |
18 | * async: `-D 'sleep 3; smtpctl show status && kill -10 ${MAINPID}'`
19 | * halt: `-H 'if [[ ${_exit_status} -eq 138 ]]; then exit 0; else exit ${_exit_status}; fi'`
20 |
21 | That's how it looks like:
22 |
23 | ```bash
24 | $ docker run --rm vorakl/centos-opensmtpd -H 'if [[ ${_exit_status} -eq 138 ]]; then exit 0; else exit ${_exit_status}; fi' -D 'sleep 3; smtpctl show status && kill -10 ${MAINPID}'
25 |
26 | 2017-03-16 21:40:50 trc [main/1]: The wait policy: wait_any
27 | 2017-03-16 21:40:50 trc [async/15]: Launching in the background: /etc/trc.d/async.opensmtpd
28 | 2017-03-16 21:40:50 trc [async/16]: Launching in the background: sleep 3; smtpctl show status && kill -10 ${MAINPID}
29 | info: OpenSMTPD 6.0.2p1 starting
30 | setup_peer: klondike -> control[28] fd=4
31 | setup_peer: klondike -> pony express[30] fd=5
32 | setup_done: ca[27] done
33 | setup_proc: klondike done
34 | setup_peer: control -> klondike[27] fd=5
35 | setup_peer: control -> lookup[29] fd=6
36 | setup_peer: control -> pony express[30] fd=7
37 | setup_peer: control -> queue[31] fd=8
38 | setup_peer: control -> scheduler[32] fd=9
39 | setup_done: control[28] done
40 | setup_proc: control done
41 | setup_peer: scheduler -> control[28] fd=9
42 | setup_peer: scheduler -> queue[31] fd=10
43 | setup_peer: lookup -> control[28] fd=6
44 | setup_peer: lookup -> pony express[30] fd=7
45 | setup_peer: lookup -> queue[31] fd=8
46 | setup_done: lka[29] done
47 | setup_proc: lookup done
48 | setup_peer: pony express -> control[28] fd=7
49 | setup_peer: queue -> control[28] fd=8
50 | setup_peer: pony express -> klondike[27] fd=8
51 | setup_peer: queue -> pony express[30] fd=9
52 | setup_peer: queue -> lookup[29] fd=10
53 | setup_peer: queue -> scheduler[32] fd=11
54 | setup_peer: pony express -> lookup[29] fd=9
55 | setup_peer: pony express -> queue[31] fd=10
56 | setup_done: pony[30] done
57 | setup_proc: pony express done
58 | setup_done: queue[31] done
59 | setup_proc: scheduler done
60 | setup_done: scheduler[32] done
61 | smtpd: setup done
62 | warn: purge_task: opendir: No such file or directory
63 | setup_proc: queue done
64 | MDA running
65 | MTA running
66 | SMTP running
67 | 2017-03-16 21:40:53 trc [async/16]: Exiting in the background (exitcode=0): sleep 3; smtpctl show status && kill -10 ${MAINPID}
68 | 2017-03-16 21:40:53 trc [main/1]: Trying to terminate sub-processes...
69 | 2017-03-16 21:40:53 trc [main/1]: terminating the child process
70 | info: Terminated, shutting down
71 | info: control process exiting
72 | info: ca agent exiting
73 | info: pony agent exiting
74 | info: queue handler exiting
75 | info: lookup agent exiting
76 | info: scheduler handler exiting
77 | warn: parent terminating
78 | 2017-03-16 21:40:53 trc [async/15]: Exiting in the background (exitcode=0): /etc/trc.d/async.opensmtpd
79 | 2017-03-16 21:40:54 trc [halt/60]: Running the shutdown command: if [[ ${_exit_status} -eq 138 ]]; then exit 0; else exit ${_exit_status}; fi
80 | 2017-03-16 21:40:54 trc [main/1]: Exiting from the shutdown command (exitcode=0): if [[ ${_exit_status} -eq 138 ]]; then exit 0; else exit ${_exit_status}; fi
81 | 2017-03-16 21:40:54 trc [main/1]: Exited (exitcode=0)
82 | ```
83 |
--------------------------------------------------------------------------------
/examples/self-configuring/README.md:
--------------------------------------------------------------------------------
1 | # In this tricky example will be shown how to create configuration on the fly
2 |
3 | In the begging, it has only the `boot` stage.
4 | As long as scripts in this stages run in the same environment with the main process, it can bootstrap itself by creating missing configuration. There to ways:
5 | * create configuration files
6 | * modify command line
7 |
8 | In this example, will be shown how to modify a command line, to print out the famous "Hello, World!"
9 | Because the whole bootstrapping logic will reside in one boot file, there are no reasons to create a configuration directory.
10 | That's why there is only one file `trc.boot.make-self-conf`.
11 | And don't forget to change a wait policy, otherwise you won't see the result of executing of all commands!
12 | Let's run it:
13 |
14 | ```bash
15 | $ RC_WAIT_POLICY=wait_all trc -w .
16 | Hello World
17 | ```
18 |
--------------------------------------------------------------------------------
/examples/self-configuring/trc.boot.make-self-conf:
--------------------------------------------------------------------------------
1 | set -- "$@" -F 'echo -n "Hello "' -F 'echo "World"'
2 |
--------------------------------------------------------------------------------
/examples/sync-run-of-async-groups/README.md:
--------------------------------------------------------------------------------
1 | # Serial launching of group of parallel processes and fails immediately if some group failed
2 |
3 | This example uses the idea of ["create configuration on the fly"](https://github.com/vorakl/TrivialRC/tree/master/examples/self-configuring) example. The trick is to run one TrivialRC with one wait policy a group of other TrivialRCs with another wait policy. The goal of a first copy of TrivialRC is:
4 |
5 | * to construct a proper list of sync tasks
6 | * itereate over the list checking exit status after each task (because this trc is run with 'wait_err' wait policy)
7 |
8 | Each sync task runs another copy of TrivialRC with another wait policy ('wait_all') to iterate over a smaller group of IP address (or other parameters from a command line) by running commands in parallel. All final commands will be forms at the `boot` stage in the same way.
9 |
10 | So, it would look like:
11 |
12 | ```bash
13 | RC_WAIT_POLICY=wait_err trc --workdir .
14 | \- RC_WAIT_POLICY=wait_all trc --workdir sub-trc IP1 IP2 IP3 ... IP10
15 | a \- cmd IP1
16 | s \- cmd IP2
17 | y \- cmd IP3
18 | n \- .......
19 | c \- cmd IP10
20 | s \- .....
21 | y \- .....
22 | n \- .....
23 | c \- RC_WAIT_POLICY=wait_all trc --workdir sub-trc IP1 IP2 IP3 ... IP10
24 | a \- cmd IP1
25 | s \- cmd IP2
26 | y \- cmd IP3
27 | n \- .......
28 | c \- cmd IP10
29 | ```
30 | The main idea is to have only boot scripts for bootstrapping subsequent behavior of the first TrivialRC copy by
31 | running one by one another copy of TrivialRC which eventually runs commands in parallel (one command per one IP).
32 |
33 | A number of parallel tasks and a command can be changed by environment variables ASYNC_TASKS and TASK_CMD accordingly.
34 |
35 | ## Test
36 |
37 | ```bash
38 | $ RC_WAIT_POLICY=wait_err trc -w .
39 |
40 | $ TASK_CMD="uname -a" RC_WAIT_POLICY=wait_err trc -w .
41 |
42 | $ ASYNC_TASKS=20 TASK_CMD="id -un" RC_WAIT_POLICY=wait_err trc -w .
43 | ```
44 |
--------------------------------------------------------------------------------
/examples/sync-run-of-async-groups/sub-trc/trc.boot.async-run-cmds:
--------------------------------------------------------------------------------
1 | local _ip _params
2 |
3 | for _ip in "$@"; do
4 | _params="${_params}-D 'ssh -i admin.key -o StrictHostKeyChecking=no admin@${_ip} ${TASK_CMD:-uptime}' "
5 | done
6 |
7 | # replaces all command line parameters
8 | eval set -- ${_params}
9 |
--------------------------------------------------------------------------------
/examples/sync-run-of-async-groups/trc.boot.serial-run-groups:
--------------------------------------------------------------------------------
1 | # The script gets IP address,
2 | # converts them to lines by ${ASYNC_TASKS} elements (or 10 by default),
3 | # iterates over the list and for each new line
4 | # forms a part of a future command line for itself and then
5 | # updates its own command line to have a number of sync process when
6 | # it has moved to the next stage.
7 |
8 | local _ips _params
9 |
10 | while read _ips; do
11 | _params="${_params}-F 'RC_WAIT_POLICY=wait_all trc -w sub-trc ${_ips}' "
12 | done < <( curl -sSLf 'http://service-discovery.domain.com/api/nodes' | \
13 | jq -r '.[] | select(.ipaddr != null) | .ipaddr' | \
14 | xargs -n ${ASYNC_TASKS:-10} echo )
15 |
16 | eval set -- "$@" ${_params}
17 |
--------------------------------------------------------------------------------
/src.docs/.gitignore:
--------------------------------------------------------------------------------
1 | **/__pycache__/
2 | py3/
3 | *.pyc
4 |
--------------------------------------------------------------------------------
/src.docs/Makefile:
--------------------------------------------------------------------------------
1 | PY?=python
2 | PELICAN?=pelican
3 | PELICANOPTS=
4 |
5 | BASEDIR=$(CURDIR)
6 | INPUTDIR=$(BASEDIR)/content
7 | OUTPUTDIR=$(shell readlink -f ../docs)
8 | STATICDIR=$(BASEDIR)/theme/static
9 | THEMEDIR=/home/vorakl/repos/my/github/aves/theme
10 | PLUGINSDIR=/home/vorakl/repos/others/pelican-plugins
11 | CONFFILE=pelicanconf.py
12 | PUBLISHCONF=publishconf.py
13 |
14 | UID=$(shell id -u)
15 | GID=$(shell id -g)
16 |
17 | GITHUB_PAGES_BRANCH=master
18 |
19 | PORT ?= 8000
20 | DEBUG ?= 0
21 | ifeq ($(DEBUG), 1)
22 | PELICANOPTS += -D
23 | endif
24 |
25 | RELATIVE ?= 0
26 | ifeq ($(RELATIVE), 1)
27 | PELICANOPTS += --relative-urls
28 | endif
29 |
30 | .PHONY: html help serve publish github
31 |
32 | help:
33 | @echo 'Makefile for a pelican Web site '
34 | @echo ' '
35 | @echo 'Usage: '
36 | @echo ' make html (re)generate the web site '
37 | @echo ' make publish generate using production settings '
38 | @echo ' make bundle generate bundled version of CSS and JS '
39 | @echo ' make serve [PORT=8000] serve site at http://localhost:8000/ '
40 | @echo ' make github upload the web site via gh-pages '
41 | @echo ' '
42 | @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html '
43 | @echo 'Set the RELATIVE variable to 1 to enable relative urls '
44 | @echo ' '
45 |
46 | html:
47 | @if docker version &>/dev/null; then \
48 | echo 'Building a local web-site using docker image'; \
49 | docker run \
50 | --rm \
51 | -u $(UID):$(GID) \
52 | -v $(BASEDIR):/site \
53 | -v $(INPUTDIR):/input \
54 | -v $(OUTPUTDIR):/output \
55 | -v $(THEMEDIR):/theme \
56 | -v $(PLUGINSDIR):/plugins \
57 | vorakl/alpine-pelican \
58 | -F 'cd /site && pelican /input -o /output -t /theme -s $(CONFFILE) $(PELICANOPTS)'; \
59 | else \
60 | echo 'Building a local web-site using a local Pelican'; \
61 | @$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(BASEDIR)/$(CONFFILE) $(PELICANOPTS); \
62 | fi
63 |
64 | bundle:
65 | @cd $(STATICDIR) && \
66 | cat bootstrap.min.css bootstrap.min.responsive.css local.min.css pygments.min.css > bootstrap-pygments.bundle.min.css && \
67 | cat jquery.min.js bootstrap-collapse.min.js > jquery-bootstrap-collapse.bundle.min.js
68 |
69 | serve:
70 | @if docker version &>/dev/null; then \
71 | echo 'Launching a docker container with Nginx on the port $(PORT)'; \
72 | docker run \
73 | --rm \
74 | -it \
75 | -p $(PORT):80 \
76 | -v $(OUTPUTDIR):/usr/share/nginx/html \
77 | nginx:mainline-alpine; \
78 | else \
79 | echo 'Launching Pelican Server on port $(PORT)'; \
80 | cd $(OUTPUTDIR) && $(PY) -m pelican.server $(PORT); \
81 | fi
82 |
83 | publish:
84 | @if docker version &>/dev/null; then \
85 | echo 'Building a public web-site using docker image'; \
86 | docker run \
87 | --rm \
88 | -u $(UID):$(GID) \
89 | -v $(BASEDIR):/site \
90 | -v $(INPUTDIR):/input \
91 | -v $(OUTPUTDIR):/output \
92 | -v $(THEMEDIR):/theme \
93 | -v $(PLUGINSDIR):/plugins \
94 | vorakl/alpine-pelican \
95 | -F 'cd /site && pelican /input -o /output -t /theme -s $(PUBLISHCONF) $(PELICANOPTS)'; \
96 | else \
97 | echo 'Building a public web-site using a local Pelican'; \
98 | @$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(BASEDIR)/$(PUBLISHCONF) $(PELICANOPTS); \
99 | fi
100 |
101 | github: publish
102 | @cd $(OUTPUTDIR) && \
103 | git add . && \
104 | git commit -m "New content" && \
105 | git push origin $(GITHUB_PAGES_BRANCH)
106 |
107 |
--------------------------------------------------------------------------------
/src.docs/content/images/trivialrc.xml:
--------------------------------------------------------------------------------
1 | 3V1bc6M2FP41nmlfPAhxfdxNt22mu9tMtzO7ecoQm7XpYssDuE766wtGwnCOiAnIwiEPiS0EiI/vXHTOkTKjN5un35Jgt/7ElmE8M43l04z+MjNNQnwz/1O0PJctjmmVDaskWvJOp4Yv0X8hbzR46z5ahmmjY8ZYnEW7ZuOCbbfhImu0BUnCDs1u31ncvOsuWIWo4csiiHHr12iZrcVzGcbpwO9htFrzW3s2P/AYLH6sErbf8vvNTPr9+FMe3gTiWrx/ug6W7FBroh9m9CZhLCs/bZ5uwrjAVsBWnvdry9Fq3Em4zbqcYPEXlWbP4tnDZQ4F/8qSbM1WbBvEH06t74/PFxZXMPJv62wT5x9J/jG/afL8rWif2+LrPe/2T5hlz/xNB/uM5U2nq39kbMevkWYJ+1GhTvOWcoTFsFofkjelbJ8sRC+H8yZIViHvZtIK4py6IduE+QDzPkkYB1n0b/P6AefQqup3wjH/wKFseQ/85v8G8Z5fNc0HkmGw4zjndAHqYR1l4ZddcBz/IRerJrT8emGShU8vw4Afj59g2eUZXCYtTsHDieAub1rXqC2YOgQO078MyxocO1JON8tKRgGWWbpYRpVKbz/siFLsCG3hsHLwxN1rIppLVxqx7cx04nw47x+T/NMqO8KA29ZhvEPoJ2u2edynesTZaIizh8XZk4izp0CcLe/ajEZPAlqYgJariX/2FcjuAL0ngc60dYmuhUT3wJIfhb8ZJaPKJPFHE0pK0JO/JT7ZEj45mvjkXJ0TrA5ER5s9tZFQPhbTmDHFkY5nIz37Mpy6ApdXMrHyDPl7UM4y/wq03AABHarR+Kl3LMrvUtHcNps0Nx1A4PJV8bNOYL9LkuC51m1XdEjR66gG2k0P4KnvOojH1QOWN55ZppPVA67EZHua9AC5VEhhbhEXWu3jl7swifJhhskrTbky3SGB29flIYmb14T6LmGLME3xxPhTsA1W4bh+uDee4SdE6exYO82GSrDcRBGDABvldrNRfSyQh8j6mD/0qIT03fEsEJ4tf2YIjPzRsuZTl4y6YTErVN6WbQuefo/iGDQFcbTa5l8XORSFdnxfABUtgvgdP7CJlssjyWUwK8CWAGpVWZoauJYEXBWRbYpnPdMCt/IlxwAXu5LTAtcak7nYpE8LXMcdEVxsgqYFrj8ic8V1G8lVhpMxY+ZWiY3guFRy1cJa8j5Mp0Q24MybOrmGtWQaFn4tnvdULacGq0hLPByTEuWx/Ha1w9AfbcwB9CcrKlN/xim1FeBqYwU5MdIidDWy1vYRulg7bpfvikqpAqM4SNNoAUIiT1H2rfb5FGia9ZpYtiVWa3DYL5Bt4PwTurEUwlwOFU0/0YWoAQyfgLZ3rLXbfURC4cSA8op9J8mEKA2g6Q5TOEMTDh3DFBbkibowhYPdGGRCwqdwIbcdEgMUzIqCyM0m2C5Hty0w3mG6tJttsYdrP4ecBzbZb1tw5QhiW6QbQpi81GmeHVwcNboBactk6zEgFiS0B5ja1YBYNjQgQ5N13e6j2oA4k592IAHU6ME5eN4xMXSRhdCIrjv1wCIxAHd1xmdcBbMPnKRuNydV8rbK5A41NSKD1CgMbfE49Zgf/5zV6Gp+4IUsmGJSZH6I4clH3DYwdIJlqjVYnoNZOI1KDAFcoyJLV92fuHlN2oP0ebtAYOtMtzpg9qyz8M+dLM3ESrx6wY+ucnkPT+pGZ5lrjscybGOn5cHY0OBpdGB8rNJQAGHB4rhYeXrlQQRY+agziCAWAU93DoPQ1UlSBSEaHI3t4GUbc7exrqF/ENeX+S2+HHE9brbtNvO4lPR0sx2zeaHhJdktA4YMVBzl8fFMGanCQxC16UFJkHqxYMky2q7i4jFzpyX/tQ7rXWvp079uHr6+u/374e7Pj7c391eZRPUosFM+VgHVZK3OWEiIXjoAR4neYqTbBKlSm+gzUsTAjuXErBScDOm0UsTABZITgxfWmhOJi3UxeAlm7+irbSAgtKM4K6m+F6hexi0yFOWy+Hsbz80BoTajp5tDQFlF13T5q6OJsHzDbgQTh1dDmFiOpjWf9kBhA6UatZRkwb4CoewqemNJGXJrRFXwYCmjeqRMDFidlGFnQKFy1pEZqgxuYxneqKVtDjC3Tt/UkAP52nGJ1mtp5r+szM/2F/5sa4WFMay/TRWX2kmWRE/MB4bLAajIoGqxLrJZsDqXT6wI5pqFzA0QDkO7fBRd4XLhfurGl+y1MLLv6PnwVffNRMPymsutCCXCYNbXL9/mv8Xkv8aU9BBt4uAoVk1SmGpEBS2cQYJyyoTXX59lqZAViv3cXZID/MD3Pvvp5267n5Un7dNgFRanwNmm+gASbSm/EL4WCLdaeP2NLwFVRRCOiD0037R6P4OvC2krCXNeSsF7E1hMdw5eoFQ1oksk68eqvR2Mts0ckJYUmM6KlQvFj1R/LoN0fdQLygTfsmnTHGHkqFjEVYeOqFGnF9rdRdX2uf1zc5Wl6FIDd4GdTSWkPBrsb/P5vXabDc1LVQxeY5kplnw2WAbzh/3AkOy+cgTjdj7/QzsYljsmGL4slHSVKS/aEhUQs+2zuzFfLOUlWZ1e8unzfP5JO59cMiqfZCGp6+RTy848V8An5QXfzQm4XZ9+z14uiuxn7WQVtH7LNt6aUjQgKOX2nWbDQjrnQlE9GIYUCYbWcZnD+iuP0km2lVDIYlWr4MZlpQtZ2bc+Cl7oUqvgHJTSsF8cF+x/jpXwOUB/BayULWZ9W7pVljFp2SpcE4tB0ZwLrWBnFiN2XYbF+D6qdZ/mYtKeum9U1sAchwuDh11Z45l6dJ/nvU73wf5id7KuzwH6v5aV+dfT/z0qu5/+uRT98D8=
--------------------------------------------------------------------------------
/src.docs/content/pages/404.rst:
--------------------------------------------------------------------------------
1 | Not Found
2 | #########
3 |
4 | :status: hidden
5 | :save_as: 404.html
6 | :slug: 404
7 |
8 | The requested item could not be located.
9 |
--------------------------------------------------------------------------------
/src.docs/content/pages/info.rst:
--------------------------------------------------------------------------------
1 |
2 | The minimalistic Run-time Configuration (RC) system and process manager
3 | #######################################################################
4 |
5 | :slug: info
6 | :summary: The minimalistic Run-time Configuration (RC) system and process manager
7 |
8 | |
9 |
10 | * Community_
11 | * Introduction_
12 | * Installation_
13 | * `The installation on top of CentOS Linux base image`_
14 | * `The installation on top of Alpine Linux base image`_
15 | * `How to get started?`_
16 | * `Command line options`_
17 | * `Run stages`_
18 | * `Wait policies`_
19 | * `Verbose levels`_
20 | * `Integrated functions`_
21 | * `Useful global variables`_
22 |
23 | |
24 |
25 | Community
26 | =========
27 |
28 | There are a few options for the communication:
29 |
30 | * ``IRC``: #trivialrc channel on the OFTC network (irc.oftc.net)
31 | * ``Mailing list``: trivialrc-dev@freelists.org, subscribe_
32 |
33 | |
34 |
35 | Introduction
36 | ============
37 |
38 | The minimalistic Run-time Configuration (RC) system and process manager is written in pure BASH and uses just a few external utilities like ``ls``, ``ps``, ``date`` and ``sleep``. Minimally, installation of TrivialRC consists of only one file which can be downloaded directly from the Github. Originaly, it was designed for use in containers but it also can be well used for running a group of processes asynchronously and synchronously, as well as managing their running order and exit codes.
39 |
40 | TrivialRC is not a replacement for an init process that usually resides in ``/sbin/init`` and has a PID 1. In containers for this purpose projects like dumb-init_ or tini_ can be used, although in most cases, having only TrivialRC as a first/main process (PID 1) in containers is quite enough. In terms of Docker, the best place for it is ENTRYPOINT.
41 |
42 | TrivialRC is an equivalent to well known ``/etc/rc`` for xBSD users. The RC system that is used for managing startup and shutdown processes. It can start and stop one or more processes, in parallel or sequentially, on back- or foreground, react differently in case of process failures, etc. All commands can be specified in the command line if they are relatively simple, or in separate files if a more comprehensive scenario is needed. That's why it can be used as a simplest tool for managing a group of process and be a lightweight replacement for solutions like Supervisor_.
43 |
44 | For instance, in docker images when TrivialRC is used as an Entrypoint, it doesn't reveal itself by default, does not affect any configuration and behaves absolutely transparently. So, you can add it into any Dockerfiles which do not have an entrypoint yet and get by this the full control under processes with fairly detailed logs of what's is going on inside a container. Please, take a look at examples__ for more information.
45 |
46 | __ https://github.com/vorakl/TrivialRC/tree/master/examples
47 |
48 | |
49 |
50 | Installation
51 | ============
52 |
53 | Basically, all you need to install TrivialRC is download the latest release of the script from ``http://trivialrc.vorakl.com/trc``
54 | and give it an execute permission. By default, it looks for configuration files in the same directory from which it was invoked but this behavior can be changed by setting a work directory (``-w|--workdir`` parametr). So, if you are going to use configuration files and keep them in ``/etc/``, then you would probably want to install the script to /etc/ as well and simply run it without specifying any parametrs.
55 |
56 | Another option in this case could be to install the script in a more appropriate path but don't forget to specify ``--workdir /etc`` parametr every time when you invoke this rc system. Both options are possible and depend more on a particular use case.
57 | For instance, in case of using in a docker container, I personaly prefer to have all configuration in separate files in ``trc.d/`` sub-directory and copy it together with the script in ``/etc/``.
58 |
59 | |
60 |
61 | The installation on top of CentOS Linux base image
62 | --------------------------------------------------
63 |
64 | This is an example of how it would look in a Dockerfile with `centos:latest`_ as base image:
65 |
66 | .. code-block:: bash
67 |
68 | FROM centos:latest
69 |
70 | RUN curl -sSLfo /etc/trc http://trivialrc.vorakl.com/trc && \
71 | ( cd /etc && curl -sSLf http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \
72 | chmod +x /etc/trc && \
73 | /etc/trc --version
74 |
75 | # Uncomment this if you have configuration files in trc.d/
76 | # COPY trc.d/ /etc/trc.d/
77 |
78 | ENTRYPOINT ["/etc/trc"]
79 |
80 | |
81 |
82 | The installation on top of Alpine Linux base image
83 | --------------------------------------------------
84 |
85 | **Attention**! The Alpine Linux comes with Busybox but its functionality as a shell and as a few emulated tools ``is not enough`` for TrivialRC. To work in this distribution it requires two extra packages: ``bash`` and ``procps``.
86 | As a result, Dockerfile for the `alpine:latest`_ base image would look like:
87 |
88 | .. code-block:: bash
89 |
90 | FROM alpine:latest
91 |
92 | RUN apk add --no-cache bash procps
93 |
94 | RUN wget -qP /etc/ http://trivialrc.vorakl.com/trc && \
95 | ( cd /etc && wget -qO - http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \
96 | chmod +x /etc/trc && \
97 | /etc/trc --version
98 |
99 | # Uncomment this if you have configuration files in trc.d/
100 | # COPY trc.d/ /etc/trc.d/
101 |
102 | ENTRYPOINT ["/etc/trc"]
103 |
104 | |
105 |
106 | How to get started?
107 | ===================
108 |
109 | To get started and find out some features, basically, I suggest to go through `all available examples`_ and read their readmes plus comments along the code but to start from `one-liners`_ which show most common use cases and features.
110 |
111 | |
112 |
113 | Command line options
114 | ====================
115 |
116 | It is important to notice that the order of command line options **is not** equal to their run order.
117 | In general it looks like:
118 |
119 | .. code-block:: bash
120 |
121 | $ trc [-h|--help] [-v|--version] [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [command [args]]
122 |
123 |
124 | Where
125 |
126 | * ``-h`` or ``--help``, prints a short help message
127 | * ``-v`` or ``--version``, prints a current version
128 | * ``-w 'directory'`` or ``--workdir 'directory'``, sets a location with configuration files
129 | * ``-B 'command1; command2; ...'``, boot commands
130 | * ``-H 'command1; command2; ...'``, halt commands
131 | * ``-D 'command1; command2; ...'``, async commands
132 | * ``-F 'command1; command2; ...'``, sync commands
133 | * ``command [args]``, a sync command
134 |
135 | So, command line options have to be supplied in the next order
136 |
137 | 1. ``-B``, zero or more
138 | 2. ``-H``, zero or more
139 | 3. ``-D``, zero or more
140 | 4. ``-F``, zero or more
141 | 5. ``command with arguments`` (without an option), zero or only one
142 |
143 | Examples:
144 |
145 | .. code-block:: bash
146 |
147 | $ trc -B 'name=$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, ${name}!'
148 |
149 | $ RC_WAIT_POLICY=wait_all trc -D 'echo Hello' -D 'sleep 2; echo World' echo waiting...
150 |
151 | $ RC_VERBOSE=true trc -F 'echo -n "Hello "; echo World'
152 |
153 | $ trc --workdir /opt/app
154 |
155 | |
156 |
157 | Run stages
158 | ==========
159 |
160 | The life cycle of TrivialRC consists of different stages, with different isolation.
161 | By default, all configuration files (or trc.d/ directory with them) are searched in the directory from which was executed ``trc`` itself. For instance, if you've installed trc in /usr/bin/ and run it by using only its name, like ``trc``, then configuration will also be searched in /usr/bin/. Though, you can place configuration files anywhere you like and specify their location in the ``-w|--workdir`` option, like ``trc -w /etc/``.
162 |
163 | Let's check:
164 |
165 | .. code-block:: bash
166 |
167 | $ which trc
168 | /usr/bin/trc
169 |
170 | $ trc -B 'echo $work_dir'
171 | /usr/bin
172 |
173 | $ trc -w /etc -B 'echo $work_dir'
174 | /etc
175 |
176 |
177 | All stages are executed through in the next order:
178 |
179 | 1. ``boot``
180 | **Execution order**: trc.boot.* -> trc.d/boot.* -> [-B 'cmds' [...]]
181 |
182 | Commands run in a same environment as the main process and that's why it has to be used with caution.
183 | It's useful for setting up global variables which are seen in all other isolated environments.
184 | 2. ``async``
185 | **Execution order**: trc.async.* -> trc.d/async.* -> [-D 'cmds' [...]]
186 |
187 | Commands run in the separate environment, asynchronously (all run in parallel), in the background and do not affect the main process.
188 | If you are going to run more than one async commands, don't forget that default RC_WAIT_POLICY is set to 'wait_any' and the executing process will be stopped after the first finished command and only if there wasn't any running foreground (sync) command that could block the reaction on the TERM signal. So, there are two options:
189 |
190 | * to wait until all async commands have finished, you need to set RC_WAIT_POLICY to 'wait_all'.
191 | * to wait for the first finished command, do not change the default value of RC_WAIT_POLICY but run only async commands.
192 | 3. ``sync``
193 | **Execution order**: trc.sync.* -> trc.d/sync.* -> [-F 'cmds' [...]] -> [cmd]
194 |
195 | Commands run in the separate environment, synchronously (one by one), in the foreground and do not affect the main process.
196 | if you are going to run more than one sync commands, don't forget to change RC_WAIT_POLICY to 'wait_all' or 'wait_err', otherwise, the executing process will be stopped after the first command.
197 | 4. ``halt``
198 | **Execution order**: trc.halt.* -> trc.d/halt.* -> [-H 'cmds' [...]]
199 |
200 | Commands run in the separate environment, synchronously (one by one) when the main process is finishing (on exit).
201 | An exit status from the last halt command has precedence under an exit status from the main process which was supplied as ${_exit_status} variable. So you are able to keep a main exit status (by finishing as **exit ${_exit_status}**) or rewrite it to something else but anyway, if you have at least one halt command, TrivialRC will finish with an exit status of this halt command.
202 |
203 | |
204 |
205 | Wait policies
206 | =============
207 |
208 | The rc system reacts differently when one of controlled processes finishes.
209 | Depending on the value of **RC_WAIT_POLICY** environment variable it makes a decision when exactly it should stop itself.
210 | The possible values are:
211 |
212 | * ``wait_all``
213 | stops after exiting all commands and it doesn't matter whether they are synchronous or asynchronous. Just keep in mind, if you need to catch a signal in the main process, it doesn't have to be blocked by some foreground (sync) process. For example, this mode can be helpful if you need to troubleshoot a container (with `wait_any` policy) where some async task fails and the whole container gets stopped by this immediately. In this case, you can change a policy to `wait_all` and run BASH in the foreground like ``docker -e RC_WAIT_POLICY=wait_all some-container bash``
214 | * ``wait_any`` [default]
215 | stops after exiting any of background commands and if there are no foreground commands working at that moment. It makes sense to use this mode if all commands are **asynchronous** (background). For example, if you need to start more than one process in the docker container, they all have to be asynchronous. Then, the main processed will be able to catch signals (for instance, from a docker daemon) and wait for finishing all other async processes.
216 | * ``wait_err``
217 | stops after the first failed command. It make sense to use this mode with **synchronous** (foreground) commands only. For example, if you need to iterate sequentially over the list of commands and to stop only if one of them has failed.
218 | * ``wait_forever``
219 | there is a special occasion when a process has doubled forked to become a daemon, it's still running but for the parent shell such process is considered as finished. So, in this mode, TrivialRC will keep working even if all processes have finished and it has to be stopped by the signal from its parent process (such as docker daemon for example).
220 |
221 | |
222 |
223 | Verbose levels
224 | ==============
225 |
226 | By default, TrivailRC doesn't print any service messages at all.
227 | It only sends ``stdout`` and ``stderr`` of all isolated sub-shells to the same terminal.
228 | If another behavior is needed, you can redirect any of them inside each sub-shell separately.
229 | To increase the verbosity of rc system there are provided a few environment variables:
230 |
231 | * ``RC_DEBUG`` (true|false) [false]
232 | Prints out all commands which are being executed
233 | * ``RC_VERBOSE`` (true|false) [false]
234 | Prints out service information
235 | * ``RC_VERBOSE_EXTRA`` (true|false) [false]
236 | Prints out additional service information
237 |
238 | |
239 |
240 | Integrated functions
241 | ====================
242 |
243 | You can also use some of internal functions in async/sync tasks:
244 |
245 | * ``say``
246 | prints only if RC_VERBOSE is set
247 | * ``log``
248 | does the same as ``say`` but add additional info about time, PID, namespace, etc
249 | * ``warn``
250 | does the say as ``log`` but sends a mesage to stderr
251 | * ``err``
252 | does the same as ``warn`` but exits with an error (exit status = 1)
253 | * ``debug``
254 | does the same as ``log`` but only if RC_VERBOSE_EXTRA is set
255 | * ``run``
256 | launches builtin or external commands without checking functions with the same name
257 | For instance, if you wanna run only external command from the standart PATH list, use ``run -p 'command'``
258 | Or, if you need to check existence of the command, try ``run -v 'command'``
259 |
260 | |
261 |
262 | Useful global variables
263 | =======================
264 |
265 | * ``MAINPID``, for sending signals to the main process (see `Testing of Docker images`_)
266 | * ``_exit_status``, for checking or rewriting an exit status of the whole script (see `Process Manager`_, `Service Discovery`_)
267 |
268 | .. Links
269 |
270 | .. |run-stages| image:: trivialrc-stages.svg
271 | :scale: 50
272 | :alt: Run Stages
273 | :align: middle
274 | .. _dumb-init: https://github.com/Yelp/dumb-init
275 | .. _tini: https://github.com/krallin/tini
276 | .. _Supervisor: https://github.com/Supervisor/supervisor
277 | .. _`centos:latest`: https://hub.docker.com/_/centos/
278 | .. _`alpine:latest`: https://hub.docker.com/_/alpine/
279 | .. _`all available examples`: https://github.com/vorakl/TrivialRC/tree/master/examples
280 | .. _`one-liners`: https://github.com/vorakl/TrivialRC/blob/master/examples/one-liners
281 | .. _`Testing of Docker images`: https://github.com/vorakl/TrivialRC/tree/master/examples/reliable-tests-for-docker-images
282 | .. _`Process Manager`: https://github.com/vorakl/TrivialRC/blob/master/examples/process-manager/trc.d/halt.remove-logs
283 | .. _`Service Discovery`: https://github.com/vorakl/TrivialRC/blob/master/examples/docker-service-discovery/trc.d/halt.sd-unreg
284 | .. _subscribe: https://www.freelists.org/list/trivialrc-dev
285 |
--------------------------------------------------------------------------------
/src.docs/content/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/src.docs/content/static/favicon.ico
--------------------------------------------------------------------------------
/src.docs/content/static/google747986e3861ca881.html:
--------------------------------------------------------------------------------
1 | google-site-verification: google747986e3861ca881.html
--------------------------------------------------------------------------------
/src.docs/content/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /unknown/
3 |
4 | Sitemap: https://trivialrc.cf/sitemap.xml
5 |
--------------------------------------------------------------------------------
/src.docs/pelicanconf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*- #
3 | from __future__ import unicode_literals
4 |
5 | ### Basic configuration
6 | ########################
7 |
8 | AUTHOR = u'vorakl'
9 | SITENAME = u"TrivialRC"
10 | SITEURL = 'https://trivialrc.vorakl.com'
11 | SITEDESC = u'The minimal Run-time Configuration (RC) system and process manager'
12 | SITE_VERSION = '1703025594'
13 | SITE_KEYWORDS = 'trivialrc,trc,rc system,process manager,rc'
14 | ARTICLE_PATHS = ['articles'] # a place for articles under the content location
15 | PAGE_PATHS = ['pages']
16 | CONTACT_URL = 'https://vorakl.com/pages/about/'
17 | START_URL = 'pages/info/' # What's a start point of a site (like 'news/' or 'pages/about/')?
18 | TIMEZONE = 'America/Los_Angeles'
19 | DEFAULT_LANG = u'en'
20 | RELATIVE_URLS = True # disable in public version
21 | DEFAULT_DATE = 'fs'
22 | DEFAULT_DATE_FORMAT = '%Y-%m-%d'
23 | PLUGIN_PATHS = ['/plugins']
24 | PLUGINS = ['post_stats', 'pelican_youtube', 'minify'] # keep 'minify' plugin as the last element in the list to minify all output HTMLs
25 |
26 | DEFAULT_PAGINATION = 10 # Turns on the pagination
27 | PAGINATION_PATTERNS = (
28 | (1, '{base_name}/', '{base_name}/index.html'),
29 | (2, '{base_name}/p{number}/', '{base_name}/p{number}/index.html'),
30 | )
31 |
32 | DELETE_OUTPUT_DIRECTORY = True # build an output dir from scratch every time
33 | OUTPUT_RETENTION = ["CNAME", "trc", "trc.sha256"] # but these dirs and files should be kept
34 |
35 |
36 | ### Interface configuration
37 | ############################
38 |
39 | DISPLAY_MENU = True
40 | DISPLAY_PAGES_IN_MENU = False
41 | DISPLAY_CATEGORIES_ON_MENU = False
42 | DISPLAY_ITEMS_ON_MENU = True # Items are set in the MENUITEMS variable below
43 |
44 | DISPLAY_SIDEBAR = False
45 | DISPLAY_ARCHIVES_ON_SIDEBAR = False # It also turns on/off an appropriate section in a sitemap.xml
46 | DISPLAY_CATEGORIES_ON_SIDEBAR = False # It also turns on/off an appropriate section in a sitemap.xml
47 | DISPLAY_TAGS_ON_SIDEBAR = False # It also turns on/off an appropriate section in a sitemap.xml
48 | DISPLAY_PAGES_ON_SIDEBAR = True # It also turns on/off an appropriate section in a sitemap.xml
49 | DISPLAY_AUTHORS_ON_SIDEBAR = False # It's turned off because I'm the only one author on this site
50 | DISPLAY_SUBSCRIBES_ON_SIDEBAR = False
51 | DISPLAY_SITE_ON_SIDEBAR = False
52 | DISPLAY_LINKS_ON_SIDEBAR = False # Links are set in the LINKS variable below
53 |
54 | MENUITEMS = [
55 | ("repo", "https://github.com/vorakl/TrivialRC"),
56 | ("blog", "https://vorakl.com/"),
57 | ("author", "https://vorakl.com/pages/about/"),
58 | ]
59 | #LINKS = [("Github", "https://github.com/vorakl"), ("LinkedIn", "https://linkedin.com/in/vorakl/")]
60 | DISPLAY_AUTHOR = False # Add an author in a article's metadata
61 |
62 | CATEGORIES_DESCRIPTION = {}
63 | TAGS_DESCRIPTION = {}
64 |
65 | ### Feed's specification
66 | #########################
67 |
68 | FEED_EMAIL = None # disable in development version
69 | FEED_DOMAIN = '' # and create all feed under the local domain for testing purpose
70 | FEED_MAX_ITEMS = 15
71 | FEED_ALL_ATOM = ''
72 | FEED_ALL_RSS = None # Here is used the only one feed on Google's feedburner. All other feeds are disabled
73 | CATEGORY_FEED_ATOM = None
74 | CATEGORY_FEED_RSS = None
75 | TRANSLATION_FEED_ATOM = None
76 | AUTHOR_FEED_ATOM = None
77 | AUTHOR_FEED_RSS = None
78 | TAG_FEED_ATOM = None
79 | TAG_FEED_RSS = None
80 |
81 |
82 | ### Static files (non templates)
83 | #################################
84 |
85 | STATIC_PATHS = [
86 | 'static/robots.txt',
87 | 'static/google747986e3861ca881.html',
88 | 'static/favicon.ico',
89 | 'images',
90 | ]
91 | # and sprecial output paths for them
92 | EXTRA_PATH_METADATA = {
93 | 'static/robots.txt': {'path': 'robots.txt'},
94 | 'static/google747986e3861ca881.html': {'path': 'google747986e3861ca881.html'},
95 | 'static/favicon.ico': {'path': 'favicon.ico'},
96 | }
97 |
98 |
99 | ### Templates for html pages
100 | #############################
101 |
102 | # blog posts related pages
103 |
104 | # If there is a 'Save_as' metadata (like in 404.html), then a page will be rendered anyway
105 | ARTICLE_SAVE_AS = '' # activates rendering each article
106 | ARTICLE_URL = '{category}/{slug}/'
107 | ARTICLE_LANG_SAVE_AS = ''
108 | ARTICLE_LANG_URL = '{category}/{slug}-{lang}/'
109 | DRAFT_SAVE_AS = '' # activates rendering each article's draft
110 | DRAFT_URL = 'drafts/{category}/{slug}/'
111 | DRAFT_LANG_SAVE_AS = 'drafts/{category}/{slug}-{lang}/index.html'
112 | DRAFT_LANG_URL = 'drafts/{category}/{slug}-{lang}/'
113 | PAGE_SAVE_AS = 'pages/{slug}/index.html' # activates rendering each page.
114 | PAGE_URL = 'pages/{slug}/'
115 | PAGE_LANG_SAVE_AS = 'pages/{slug}-{lang}/index.html'
116 | PAGE_LANG_URL = 'pages/{slug}-{lang}/'
117 | CATEGORY_SAVE_AS = '' # activates rendering each category
118 | CATEGORY_URL = 'category/{slug}/'
119 | TAG_SAVE_AS = '' # activates rendering each tag
120 | TAG_URL = 'tag/{slug}/'
121 | AUTHOR_SAVE_AS = '' # activates rendering each author
122 | AUTHOR_URL = 'author/{slug}/'
123 |
124 | # site related pages
125 |
126 | # a list of templates for rendering blog posts. Not all of them, just an index and groups of entities (tags, categories, ...)
127 | # other templates for blog posts rendering (for a tag, a category, ...) are activated by *_SAVE_AS variables below
128 | DIRECT_TEMPLATES = []
129 | PAGINATED_TEMPLATES = {'tag': None, 'category': None, 'author': None}
130 |
131 | INDEX_SAVE_AS = 'news/index.html'
132 | AUTHORS_SAVE_AS = 'author/index.html' # defines where to save an authors page, it's activated by DIRECT_TEMPLATES
133 | AUTHORS_URL = 'author/'
134 | ARCHIVES_SAVE_AS = 'archives/index.html' # defines where to save an archives page, it's activated by DIRECT_TEMPLATES
135 | ARCHIVES_URL = 'archives/'
136 | TAGS_SAVE_AS = 'tag/index.html' # defines where to save a tags page, it's activated by DIRECT_TEMPLATES
137 | TAGS_URL = 'tag/'
138 | CATEGORIES_URL = 'category/' # defines where to save a categories page, it's activated by DIRECT_TEMPLATES
139 | CATEGORIES_SAVE_AS = 'category/index.html'
140 | PAGES_SAVE_AS = 'pages/index.html' # defines where to save a list of all pages, it's activated by TEMPLATE_PAGES
141 | PAGES_URL = 'pages/'
142 |
143 | YEAR_ARCHIVE_SAVE_AS = 'archives/{date:%Y}/index.html' # activates rendering an archive page per year/month/day
144 | MONTH_ARCHIVE_SAVE_AS = 'archives/{date:%Y}/{date:%m}/index.html'
145 | DAY_ARCHIVE_SAVE_AS = ''
146 |
147 | # additional pages
148 |
149 | # a hash array with an extra list of 'templates+output_filename' for rendering besides of blog posts
150 | # The output filename is needed because they don't have *_SAVE_AS variables
151 | TEMPLATE_PAGES = {'sitemap.html': 'sitemap.xml',
152 | 'start.html': 'index.html',
153 | 'pages.html': PAGES_SAVE_AS}
154 |
155 |
--------------------------------------------------------------------------------
/src.docs/publishconf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*- #
3 | from __future__ import unicode_literals
4 |
5 | # This file is only used if you use `make publish` or
6 | # explicitly specify it as your config file.
7 |
8 | import os
9 | import sys
10 | sys.path.append(os.curdir)
11 |
12 | # inherits pelicanconf configuration
13 | from pelicanconf import *
14 |
15 | RELATIVE_URLS = False
16 |
--------------------------------------------------------------------------------
/tests/config-test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env roundup
2 |
3 | describe "Checks if it works with config files"
4 |
5 | before() {
6 | cd ..
7 | }
8 |
9 | after() {
10 | cd -
11 | }
12 |
13 | it_prints_something() {
14 | output="$(RC_WAIT_POLICY=wait_all ./trc -w tests)"
15 | test "${output}" = "host: test-host; ip: 127.0.0.1. Good bye"
16 | }
17 |
--------------------------------------------------------------------------------
/tests/exitcodes-test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env roundup
2 |
3 | describe "Checks exit codes"
4 |
5 | before() {
6 | cd ..
7 | }
8 |
9 | after() {
10 | cd -
11 | }
12 |
13 | it_catches_all_exitcodes() {
14 | set +e
15 | output="$(RC_VERBOSE=true RC_WAIT_POLICY=wait_all ./trc -H 'exit 0' -D 'sleep 1; exit 2' -D 'sleep 2; exit 3' -F 'exit 0' -F 'exit 4' exit 5 | sed -n 's|^.*(exitcode=\([[:digit:]]*\)):.*$|\1|p' | tr '\n' ' ')"
16 | test "${output}" = "0 4 5 2 3 0 "
17 | }
18 |
19 | it_catches_exitcodes_till_errs_in_sync() {
20 | set +e
21 | output="$(RC_VERBOSE=true RC_WAIT_POLICY=wait_err ./trc -D 'sleep 1; exit 2' -D 'sleep 2; exit 3' -F 'exit 0' -F 'exit 4' exit 5 | sed -n 's|^.*(exitcode=\([[:digit:]]*\)):.*$|\1|p' | tr '\n' ' ')"
22 | test "${output}" = "0 4 143 143 "
23 | }
24 |
25 | it_rewrites_exitcode_on_halting_to_err() {
26 | set +e
27 | ./trc -H 'exit 11' exit 5
28 | test $? -eq 11
29 | }
30 |
31 | it_rewrites_exitcode_on_halting_to_ok() {
32 | set +e
33 | ./trc -H 'exit 0' exit 5
34 | test $? -eq 0
35 | }
36 |
37 | it_fails_on_booting_imm() {
38 | set +e
39 | ./trc -B 'exit 22' -H 'exit 0' exit 5
40 | test $? -eq 22
41 | }
42 |
43 | it_exits_143_on_async_any() {
44 | set +e
45 | output=$(RC_WAIT_POLICY=wait_any ./trc -D 'echo 11; exit 11' -D 'sleep 1; echo 22; exit 22')
46 | test $? -eq 143
47 | test "${output}" = "11"
48 | }
49 |
50 | it_exits_last_status_on_sync_any() {
51 | set +e
52 | output=$(RC_WAIT_POLICY=wait_any ./trc -F 'echo 11; exit 11' -F 'echo 22; exit 22')
53 | test $? -eq 11
54 | test "${output}" = "11"
55 | }
56 |
57 | it_exits_last_syncstatus_on_mix_any() {
58 | set +e
59 | output=$(RC_WAIT_POLICY=wait_any ./trc -D 'exit 33' -F 'echo 11; exit 11' -F 'echo 22; exit 22')
60 | test $? -eq 11
61 | test "${output}" = "11"
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/tests/modes-test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env roundup
2 |
3 | describe "Checks all available modes"
4 |
5 | before() {
6 | cd ..
7 | }
8 |
9 | after() {
10 | cd -
11 | }
12 |
13 | it_boot() {
14 | set +e
15 | ./trc -B 'e=11' -B 'exit $e'
16 | test $? -eq 11
17 | }
18 |
19 | it_async_any() {
20 | set +e
21 | output="$(RC_WAIT_POLICY=wait_any ./trc -D 'echo Hello' -D 'sleep 1; echo World')"
22 | test "${output}" = "Hello"
23 | }
24 |
25 | it_async_all() {
26 | output="$(RC_WAIT_POLICY=wait_all ./trc -D 'echo -n "Hello "' -D 'sleep 1; echo World')"
27 | test "${output}" = "Hello World"
28 | }
29 |
30 | it_sync_any() {
31 | set +e
32 | output="$(RC_WAIT_POLICY=wait_any ./trc -F 'echo Hello' echo World)"
33 | test "${output}" = "Hello"
34 | }
35 |
36 | it_sync_all() {
37 | set +e
38 | output="$(RC_WAIT_POLICY=wait_all ./trc -F 'echo -n "Hello "' -F 'echo World')"
39 | test "${output}" = "Hello World"
40 | }
41 |
42 | it_mix_all() {
43 | output="$(RC_WAIT_POLICY=wait_all ./trc -B 'export wait_sec=1' -D 'sleep ${wait_sec}; echo Hello' -F 'echo -n "World "')"
44 | test "${output}" = "World Hello"
45 | }
46 |
47 | it_isolated() {
48 | output="$(RC_WAIT_POLICY=wait_all ./trc -B 'export v1=A' -H 'echo "${v1}${v2}${v3}"' -D 'export v2=B' -F 'export v3=C' -F 'echo -n "${v1}${v2}${v3}"')"
49 | test "${output}" = "AA"
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/tests/trc.d/async.info:
--------------------------------------------------------------------------------
1 | print "host: ${MYHOSTNAME}; ip: ${MYIP}. "
2 |
--------------------------------------------------------------------------------
/tests/trc.d/boot.funcs:
--------------------------------------------------------------------------------
1 | print() {
2 | echo -en "$@"
3 | }
4 | declare -fx print
5 |
--------------------------------------------------------------------------------
/tests/trc.d/boot.setvars:
--------------------------------------------------------------------------------
1 | export MYIP="127.0.0.1"
2 | export MYHOSTNAME="test-host"
3 | export WAIT_TIME=1
4 |
--------------------------------------------------------------------------------
/tests/trc.d/halt.goodbye:
--------------------------------------------------------------------------------
1 | print "Good bye\n"
2 |
--------------------------------------------------------------------------------
/tests/trc.d/sync.sleep:
--------------------------------------------------------------------------------
1 | sleep ${WAIT_TIME}
2 |
--------------------------------------------------------------------------------
/tests/verbose-test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env roundup
2 |
3 | describe "Checks if verbose options work"
4 |
5 | before() {
6 | cd ..
7 | }
8 |
9 | after() {
10 | cd -
11 | }
12 |
13 | it_is_silent() {
14 | output="$(./trc)"
15 | test -z "${output}"
16 | }
17 |
18 | it_is_verbose() {
19 | verbose=$(RC_VERBOSE=true ./trc)
20 | verbose_extra=$(RC_VERBOSE=true RC_VERBOSE_EXTRA=true ./trc)
21 | test -n "${verbose}"
22 | test -n "${verbose_extra}"
23 | test $(echo "${verbose}" | wc -l) -ne $(echo "${verbose_extra}" | wc -l)
24 | }
25 |
--------------------------------------------------------------------------------
/trc:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # TrivialRC
4 | # The minimalistic RC system and process manager for containers and applications
5 | # Copyright (c) 2016, 2017 by vorakl
6 |
7 | trc_version="v1.1.12"
8 |
9 | set +e # Do not exit on errors by default
10 | if [[ "${RC_DEBUG}" = "true" ]]; then
11 | set -x # Turns on Debug mode
12 | fi
13 |
14 | main() {
15 | check_ver_usage "$1"
16 | check_req_bins ls ps pkill sleep date basename dirname
17 |
18 | # It's possible to exit from the last `halt` command with ${_exit_status} which comes from main()
19 | # and rewrite by this an exit status of the `halt` stage which always has a priority against main's exit status
20 | trap '_exit_status=$?; trap '_ignore_mt_ex=1' EXIT; debug "exit-trap (exitcode=${_exit_status})"; hook_main_exit' EXIT
21 | trap '_es_mt_s=$?; trap '_ignore_mt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=${_es_mt_s})"; exit ${_es_mt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
22 | trap '_es_mt_er=$?; trap '_ignore_mt_er=1' ERR; if [[ "${RC_WAIT_POLICY}" = "wait_err" ]]; then debug "err-trap (exitcode=${_es_mt_er})"; exit ${_es_mt_er}; fi' ERR
23 |
24 | local _wait_bg_cmd _file _es_m_f=0 _es_m_d=0
25 |
26 | export SELF_NAME=$(run -p basename $0 .sh) # Self name for logging purpose
27 | export MAINPID="${BASHPID}" # PID of the main process
28 | : ${RC_WAIT_POLICY:=wait_any}; export RC_WAIT_POLICY
29 |
30 | work_dir="${PWD}"
31 | boot_stage_left=0
32 | halt_cmds_exist=0
33 | halt_cmds=()
34 | child_pids="" # Collects of child processes
35 | ns="main" # Name Space
36 |
37 | case "${RC_WAIT_POLICY}" in
38 | wait_all) _wait_bg_cmd="run wait";;
39 | wait_any) _wait_bg_cmd="run wait";;
40 | wait_err) _wait_bg_cmd="run wait";;
41 | *) _wait_bg_cmd=":";;
42 | esac
43 |
44 | log "The wait policy: ${RC_WAIT_POLICY}"
45 |
46 | if [[ "$1" = "-w" ]] || [[ "$1" = "--workdir" ]]; then
47 | shift
48 | work_dir="$1"
49 | shift
50 | fi
51 | if [[ -n "${RC_WORK_DIR}" ]]; then
52 | work_dir="${RC_WORK_DIR}"
53 | else
54 | RC_WORK_DIR="${work_dir}"
55 | export RC_WORK_DIR
56 | fi
57 | log "Work dir: ${RC_WORK_DIR}"
58 |
59 | debug "Looking for \`boot\` scripts and commands..."
60 | {
61 | # Reads commands from files to run before everything
62 | for _file in $(run -p ls ${work_dir}/trc.boot.* ${work_dir}/trc.d/boot.* 2>/dev/null || true); do
63 | log "Launching in the $(ns_long ${ns}): ${_file}"
64 | set -e
65 | . ${_file}
66 | set +e
67 | done
68 |
69 | # Checks for boot tasks in the command line (all commands and their parameters should be supplied as one parameter)
70 | while [[ "$1" = "-B" ]]; do
71 | shift
72 | log "Launching in the $(ns_long ${ns}): $1"
73 | set -e
74 | eval "$1"
75 | set +e
76 | shift
77 | done
78 | }
79 | boot_stage_left=1
80 |
81 | debug "Looking for \`halt\` commands..."
82 | # Collects commands for the `halt` stage from a command line to be run on exit from the main exit trap
83 | while [[ "$1" = "-H" ]]; do
84 | shift
85 | halt_cmds_exist=1
86 | halt_cmds[${#halt_cmds[*]}]="$1"
87 | shift
88 | done
89 |
90 | debug "Looking for \`async\` scripts and commands..."
91 | # Reads commands from files to run in the background (in parallel)
92 | for _file in $(run -p ls ${work_dir}/trc.async.* ${work_dir}/trc.d/async.* 2>/dev/null || true); do
93 | (
94 | # Run this on any exit, catching the exitcode of the "main" command,
95 | # printing additional info and finishing up this sub-program with the right exitcode
96 | trap '_es_dt_ex=$?; trap '_ignore_mt_ex=1' EXIT; debug "exit-trap (exitcode=${_es_dt_ex})"; hook_sub_exit ${_es_dt_ex} "${_file}"' EXIT
97 |
98 | # In case of exit on errors (set -e) or by a signal, catch exitcode and exit with it, which
99 | # will lead to triggering an EXIT trap
100 | trap '_es_dt_s=$?; trap '_ignore_dt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_dt_s)"; exit ${_es_dt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
101 | trap '_es_dt_er=$?; trap '_ignore_dt_er=1' ERR; debug "err-trap (exitcode=$_es_dt_er)"; exit ${_es_dt_er}' ERR
102 |
103 | ns="async"
104 | log "Launching in the $(ns_long ${ns}): ${_file}"
105 | . ${_file}
106 | )&
107 | child_pids="${child_pids} $!"
108 | done
109 |
110 | # Checks for background tasks in the command line (all commands and their parameters should be supplied as one parameter)
111 | while [[ "$1" = "-D" ]]; do
112 | shift
113 | (
114 | trap '_es_dt_ex=$?; trap '_ignore_dt_ex=1' EXIT; debug "exit-trap (exitcode=${_es_dt_ex})"; hook_sub_exit ${_es_dt_ex} "$1"' EXIT
115 | trap '_es_dt_s=$?; trap '_ignore_dt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_dt_s)"; exit ${_es_dt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
116 | trap '_es_dt_er=$?; trap '_ignore_dt_er=1' ERR; debug "err-trap (exitcode=$_es_dt_er)"; exit ${_es_dt_er}' ERR
117 |
118 | ns="async"
119 | log "Launching in the $(ns_long ${ns}): $1"
120 | eval "$1"
121 | )&
122 | child_pids="${child_pids} $!"
123 | shift
124 | done
125 |
126 | debug "Looking for \`sync\` scripts and commands..."
127 | # Checks for foreground tasks in files (sequentially)
128 | for _file in $(run -p ls ${work_dir}/trc.sync.* ${work_dir}/trc.d/sync.* 2>/dev/null || true); do
129 | (
130 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "${_file}"' EXIT
131 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
132 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR
133 |
134 | ns="sync"
135 | log "Launching in the $(ns_long ${ns}): ${_file}"
136 | . ${_file}
137 | )
138 | # Catch the exitcode of a foreground sub-program
139 | _es_m_f=$?
140 | done
141 |
142 | # Checks for foreground tasks in the command line (all commands and their parameters should be supplied as one parameter)
143 | while [[ "$1" = "-F" ]]; do
144 | shift
145 | (
146 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "$1"' EXIT
147 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
148 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR
149 |
150 | ns="sync"
151 | log "Launching in the $(ns_long ${ns}): $1"
152 | eval "$1"
153 | )
154 | _es_m_f=$?
155 | shift
156 | done
157 |
158 | # Checks for a foreground task in the command line (without any quotation!)
159 | if [[ -n "$*" ]]; then
160 | (
161 | set -e
162 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "$@"' EXIT
163 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS
164 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR
165 |
166 | ns="sync"
167 | log "Launching in the $(ns_long ${ns}): $@"
168 | "$@"
169 | )
170 | # Catch the exitcode of a foreground sub-program
171 | _es_m_f=$?
172 | fi
173 |
174 | # Wait for all background processes and exit with a status of the last one
175 | # or with 128+SIGNAL in case of getting a signal
176 | # this is a work around for not trigerring ERR trap in main() two times in case of non 0 exist status
177 | ${_wait_bg_cmd} ${child_pids} || _es_m_d=$?
178 |
179 | if [ ${_es_m_d} -ne 0 ]; then
180 | _es_m_f=${_es_m_d} # update exit code only if one of background processes has failed
181 | fi
182 |
183 | if [[ "${RC_WAIT_POLICY}" = "wait_forever" ]]; then
184 | infinite_loop
185 | fi
186 |
187 | return ${_es_m_f}
188 | }
189 |
190 | say() {
191 | if [[ "${RC_VERBOSE}" = "true" ]]; then
192 | run echo "$@"
193 | fi
194 | }
195 |
196 | log() {
197 | say "$(run -p date '+%Y-%m-%d %H:%M:%S') ${SELF_NAME} [${ns}/${BASHPID}]: $@"
198 | }
199 |
200 | warn() {
201 | log "$@" >&2
202 | }
203 |
204 | err() {
205 | warn "$@"
206 | exit 1
207 | }
208 |
209 | debug() {
210 | if [[ "${RC_VERBOSE_EXTRA}" = "true" ]]; then
211 | log " - $@"
212 | fi
213 | }
214 |
215 | hook_main_exit() {
216 | local _pid _cpids _file _cmd _es_mtt_h=0 _term_timeout=5
217 |
218 | # If it fails in the `boot` stage, it appears here with 'set -e'. So, reset to +e again
219 | set +e
220 | trap 'true' ERR
221 |
222 | log "Trying to terminate sub-processes..."
223 | for _pid in ${child_pids}; do
224 | if run -p ps -p ${_pid} &> /dev/null; then
225 | log "terminating the child process "
226 |
227 | # Removes all unexpected sub-processes
228 | _cpids="$(run -p pgrep -P ${_pid} -d,)"
229 | run kill -TERM ${_pid} $(echo "${_cpids}" | tr ',' ' ') &> /dev/null
230 |
231 | while run -p ps -p ${_pid},${_cpids} &> /dev/null && [[ ${_term_timeout} -gt 0 ]]
232 | do
233 | run -p sleep 1
234 | : $((_term_timeout--))
235 | done
236 |
237 | if run -p ps -p ${_pid},${_cpids} &>/dev/null; then
238 | run kill -KILL ${_pid} $(run echo "${_cpids}" | run -p tr ',' ' ') &> /dev/null
239 | fi
240 | fi
241 | done
242 | debug "removing all unexpected sub-processes"
243 | if ! run -p pkill -KILL -P ${MAINPID} &> /dev/null; then
244 | true
245 | fi
246 |
247 | # --- `halt`
248 | if [[ boot_stage_left -eq 1 ]]; then
249 | debug "Looking for \`halt\` scripts..."
250 | # Checks for shutdown tasks in files (sequentially)
251 | for _file in $(run -p ls ${work_dir}/trc.halt.* ${work_dir}/trc.d/halt.* 2>/dev/null || true); do
252 | halt_cmds_exist=1
253 | (
254 | set -e # Exit on errors in the sub-shell
255 | ns="halt"
256 | log "Running the shutdown script: ${_file}"
257 | . ${_file}
258 | )
259 | _es_mtt_h=$?
260 | log "Exiting from the shutdown script (exitcode=${_es_mtt_h}): ${_file}"
261 | done
262 |
263 | # Runs shutdown tasks from the command line if any
264 | for _cmd in "${halt_cmds[@]}"; do
265 | (
266 | set -e # Exit on errors in the sub-shell
267 |
268 | ns="halt"
269 | log "Running the shutdown command: ${_cmd}"
270 | eval "${_cmd}"
271 | )
272 | _es_mtt_h=$?
273 | log "Exiting from the shutdown command (exitcode=${_es_mtt_h}): ${_cmd}"
274 | done
275 | fi
276 |
277 | if [[ ${halt_cmds_exist} -eq 1 ]]; then
278 | _exit_status=${_es_mtt_h}
279 | fi
280 |
281 | log "Exited (exitcode=${_exit_status})"
282 | exit ${_exit_status}
283 | }
284 |
285 | hook_sub_exit() {
286 | set +e # do not stop or errors anyway
287 |
288 | local _rc=$1 # Getting the exit code for a logging purpose only
289 | shift
290 |
291 | log "Exiting in the $(ns_long ${ns}) (exitcode=${_rc}): $@"
292 |
293 | if [[ "${RC_WAIT_POLICY}" = "wait_any" ]]; then
294 | # If exiting from a bg process and don't need to wait other processes, let's stop the main
295 | if run -p ps -p ${MAINPID} &> /dev/null; then
296 | debug "terminating the main process "
297 | run kill -TERM ${MAINPID} &> /dev/null
298 | fi
299 | fi
300 |
301 | exit ${_rc}
302 | }
303 |
304 | infinite_loop() {
305 | log "Activated infinite loop! To stop, press or send SIGTERM..."
306 | while true; do
307 | run -p sleep 1
308 | done
309 | }
310 |
311 | ns_long() {
312 | local _ns
313 |
314 | case "$1" in
315 | fg|sync) _ns="foreground";;
316 | bg|async) _ns="background";;
317 | sd|halt) _ns="shutdown";;
318 | main) _ns="boot";;
319 | *) _ns="$1";;
320 | esac
321 |
322 | echo "${_ns}"
323 | }
324 |
325 | check_ver_usage() {
326 | case "$1" in
327 | -v|--version) print_version ;;
328 | -h|--help) print_usage ;;
329 | *) : ;;
330 | esac
331 | }
332 |
333 | print_usage() {
334 | local _self=$0
335 | run echo "Usage: ${_self} [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [cmd [args]]"
336 | run echo "Examples:"
337 | run echo -e " $ ${_self} -B 'name=\$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, \${name}!'"
338 | run echo -e " $ RC_WAIT_POLICY=wait_all ${_self} -D 'echo Hello' -D 'sleep 2; echo World' echo waiting..."
339 | run echo -e " $ RC_VERBOSE=true ${_self} -F 'echo -n \"Hello \"; echo World'"
340 | run echo -e " $ ${_self} --workdir /opt/app"
341 | exit 0
342 | }
343 |
344 | print_version() {
345 | run echo "trc (TrivialRC) $trc_version"
346 | exit 0
347 | }
348 |
349 | run() {
350 | builtin command "$@"
351 | }
352 |
353 | check_req_bins() {
354 | local _cmd
355 |
356 | for _cmd in "$@"; do
357 | run -v ${_cmd} &> /dev/null || { echo "There is no the required command: ${_cmd}" >&2; exit 1; }
358 | done
359 | }
360 |
361 | main "$@"
362 |
--------------------------------------------------------------------------------
/version:
--------------------------------------------------------------------------------
1 | v1.1.12
2 |
--------------------------------------------------------------------------------