Compare commits
47 Commits
c4e984a709
...
dev
Author | SHA1 | Date | |
---|---|---|---|
c3d3ad26ce | |||
d4df0a1abd | |||
e0b228799f | |||
bd6a026e9c | |||
7dc36feaa3 | |||
39019f5df0 | |||
db97400377 | |||
79465f89d4 | |||
2a57f86add | |||
3462964cb0 | |||
8ccd734c02 | |||
97cf440ce5 | |||
1447b62755 | |||
ef6c079a09 | |||
6eb98de362 | |||
4860335cae | |||
348eddd3da | |||
972b24c411 | |||
b99622b2cf | |||
007fc6ac10 | |||
c0bbec80e1 | |||
96f1fa0b8c | |||
c8ba6f3d1b | |||
88e070d88c | |||
ca93b8f56d | |||
c0edb54370 | |||
4298f5e237 | |||
e06365b387 | |||
fce4e3ce0a | |||
badb4879b0 | |||
7498c87fe4 | |||
8720eedcec | |||
2669ec4e9b | |||
548bbdf503 | |||
8b08aab4e7 | |||
96418fbbb5 | |||
679b94b11e | |||
da0b56e674 | |||
6459d3d7a3 | |||
0dc48c6f9f | |||
2df2aa29a1 | |||
7e33632132 | |||
1c374d0f0b | |||
9a91efa77b | |||
2a6ab88e34 | |||
01fe246326 | |||
f546ca8817 |
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
*.egg-info
|
||||
*.swp
|
||||
__pycache__
|
||||
build
|
||||
venv
|
339
LICENSE.txt
Normal file
339
LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
330
README.md
330
README.md
@@ -1,65 +1,139 @@
|
||||
RUNON - Run your commands in any systems
|
||||
========================================
|
||||
|
||||
(c) 2021 Gilles Grandou <gilles@grandou.net>
|
||||
licensed under GPL-2.0.
|
||||
|
||||
home: https://git.grandou.net/gilles/runon
|
||||
|
||||
Runon is a frontend to podman allowing to run seamlessly any application in
|
||||
any linux based operating system, including graphical applications, while
|
||||
keeping the host user environment.
|
||||
|
||||
the v1 legacy runon branch was based on docker. Current versions are now
|
||||
relying on podman.
|
||||
|
||||
|
||||
Quick HOWTO
|
||||
-----------
|
||||
|
||||
$ grep ^PRETTY_NAME /etc/os-release
|
||||
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
|
||||
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
|
||||
|
||||
$ runon centos7 grep ^PRETTY_NAME /etc/os-release
|
||||
PRETTY_NAME="CentOS Linux 7 (Core)"
|
||||
|
||||
$ runon ubuntu20.04 grep ^PRETTY_NAME /etc/os-release
|
||||
PRETTY_NAME="Ubuntu 20.04.2 LTS"
|
||||
PRETTY_NAME="Ubuntu 20.04.6 LTS"
|
||||
|
||||
$ runon debian9 grep ^PRETTY_NAME /etc/os-release
|
||||
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
|
||||
|
||||
$ runon centos7 xclock
|
||||
[xclock launched!]
|
||||
$ runon rocky9 xterm
|
||||
[xterm launched!]
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
Installation is only tested on Debian 10, even though it should work
|
||||
straightforward on any equivalent system.
|
||||
Installation has been tested on:
|
||||
|
||||
## Docker Install
|
||||
* Debian 11 (bullseye)
|
||||
* Debian 12 (bookworm)
|
||||
* RockyLinux 9.3 (Blue Onyx)
|
||||
|
||||
sudo apt install docker
|
||||
However it should work straightforward on any equivalent system.
|
||||
|
||||
Check that your user is part of `docker` group:
|
||||
### Podman Install
|
||||
|
||||
sudo adduser <user> docker
|
||||
If not already installed, just run as root the suitable command for your system:
|
||||
|
||||
apt install podman
|
||||
dnf install podman
|
||||
|
||||
To check that podman is correctly installed, just try:
|
||||
|
||||
$ podman run -it hello-world
|
||||
Resolved "hello-world" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
|
||||
Trying to pull docker.io/library/hello-world:latest...
|
||||
Getting image source signatures
|
||||
Copying blob 719385e32844 done
|
||||
Copying config 9c7a54a9a4 done
|
||||
Writing manifest to image destination
|
||||
Storing signatures
|
||||
|
||||
Hello from Docker!
|
||||
This message shows that your installation appears to be working correctly.
|
||||
[...]
|
||||
|
||||
If your encounter an error like this one:
|
||||
|
||||
Error: writing blob: adding layer with blob "sha256:3331450fb84fde695e565405a554d5cf213a33826da197b29aabde08be012f8b": Error processing tar file(exit status 1): potentially insufficient UIDs or GIDs available in user namespace (requested 0:42 for /etc/gshadow): Check /etc/subuid and /etc/subgid: lchown /etc/gshadow: invalid argument
|
||||
|
||||
it's likely that your user account has been create a while ago on a legacy
|
||||
distribution release and has no support for `subuid` and `subgid`. You can
|
||||
fix it easily with these commands:
|
||||
|
||||
sudo usermod --add-subgids 10000-75535 $USER
|
||||
sudo usermod --add-subuids 10000-75535 $USER
|
||||
podman system migrate
|
||||
podman pull
|
||||
|
||||
If you're running on a Debian system, there is a good explanation of the above problem in `/usr/share/doc/podman/README.Debian`.
|
||||
|
||||
### Python Dependencies
|
||||
|
||||
There is no specific dependency, you just need to insure to have:
|
||||
|
||||
* a Python release 3.6 or better
|
||||
* the python `venv` module
|
||||
* the python `pip` module
|
||||
|
||||
|
||||
## manual install
|
||||
### manual install
|
||||
|
||||
cd <tools>
|
||||
git clone https://git.grandou.net/gilles/runon
|
||||
|
||||
local install, in you `~/bin` (or wherever directory which is in your
|
||||
PATH):
|
||||
local install, in your `~/.local/bin`
|
||||
|
||||
cd ~/bin
|
||||
ln -s ~/tools/runon/runon
|
||||
cd <runon>
|
||||
./install
|
||||
|
||||
mkdir ~/.config/runon
|
||||
cp <tools>/runon/runon.conf ~/.config/runon/
|
||||
If you plan to work `runon` development, you can
|
||||
pass `--dev` to install links to your current git clone:
|
||||
|
||||
system install, for all users, as `root`:
|
||||
|
||||
mkdir /etc/runon
|
||||
cp <tools>/run/runon /usr/local/bin/runon
|
||||
cp <tools>/run/runon.conf /etc/runon/
|
||||
./install --dev
|
||||
|
||||
each user can have its own configuration in `~/.config/runon/runon.conf`.
|
||||
|
||||
You can keep your configuration in several places, the 1st one which is
|
||||
find is used:
|
||||
|
||||
* `runon.yaml` in the current directory
|
||||
* `.runon.yaml` in the current directory
|
||||
* `~/.config/runon/runon.yaml`
|
||||
* `~/.config/runon/runon.default.yaml`, the configuration file installed by
|
||||
default
|
||||
|
||||
|
||||
### uninstall
|
||||
|
||||
To uninstall, just run:
|
||||
|
||||
./uninstall
|
||||
|
||||
|
||||
### some convenient links
|
||||
|
||||
you can create soft links to `runos` to simplify calls:
|
||||
|
||||
runos -l centos7
|
||||
|
||||
now calling `centos7 ...` is equivalent to call `runos centos7 ...`:
|
||||
|
||||
centos7 xterm
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
@@ -68,32 +142,61 @@ With the default configuration, a seamless environment is set up,
|
||||
allowing to transparently run commands in various environments, while
|
||||
keeping:
|
||||
|
||||
* user environment (uid, gid, password, home directory, ...)
|
||||
* user environment (uid, gid, home directory, ...)
|
||||
* password less sudo support
|
||||
* X support to run graphical applications
|
||||
|
||||
## Basic usage
|
||||
### Basic usage
|
||||
|
||||
runon [options] <osname> <command>
|
||||
|
||||
runon -h
|
||||
|
||||
## available options
|
||||
### available options
|
||||
|
||||
* `-v` verbose output, this is really usefull when running new
|
||||
containers for the first time, as the initial docker build can be
|
||||
quite long (several minutes) especially with slow internet link.
|
||||
If the command seems to be stalled, don't hesitate to interrupt it
|
||||
(with `CTRL-C`) and to restart it with `-v`.
|
||||
* `-v` verbose output, display information on the current startup step
|
||||
|
||||
* `-u` forces the container image to be updated, useful if the
|
||||
distribution has been updated and you want to use it. Otherwise,
|
||||
if a container has been already built, it will be used directly
|
||||
without doing any network access.
|
||||
if a container has been already built, it will be used directly
|
||||
without doing any network access.
|
||||
|
||||
* `-c <configfile>` uses a custom config file, useful to try new
|
||||
distribution without breaking your running config.
|
||||
|
||||
## Interactive shell
|
||||
* `-l` create an executable link with the `osname` name. you can after
|
||||
run the command with `osname [...]` instead of `runos osname [...]`
|
||||
|
||||
### Listing available distributions:
|
||||
|
||||
Just run:
|
||||
|
||||
$ runon list
|
||||
Available distributions:
|
||||
centos7
|
||||
debian10
|
||||
debian11
|
||||
debian12
|
||||
debian9
|
||||
rocky8
|
||||
rocky9
|
||||
ubuntu20.04
|
||||
ubuntu22.04
|
||||
|
||||
This lists all `osname` present in your current configuration file.
|
||||
|
||||
### Editing configuration:
|
||||
|
||||
you can easily open the current configuration file with:
|
||||
|
||||
$ runon edit
|
||||
|
||||
If you open the `runon.default.yaml` file, take care to save your changes in
|
||||
a new `runon.yaml` file to avoid the default one, which could be overwritten
|
||||
next time you install or update `runon`.
|
||||
|
||||
|
||||
### Interactive shell
|
||||
|
||||
Just run:
|
||||
|
||||
@@ -101,87 +204,101 @@ Just run:
|
||||
|
||||
while start an insteractive shell in the container system:
|
||||
|
||||
gilles@host:~$ runon centos8
|
||||
(centos8) gilles@host:~$ cat /etc/os-release
|
||||
NAME="CentOS Linux"
|
||||
VERSION="8"
|
||||
ID="centos"
|
||||
ID_LIKE="rhel fedora"
|
||||
VERSION_ID="8"
|
||||
PLATFORM_ID="platform:el8"
|
||||
PRETTY_NAME="CentOS Linux 8"
|
||||
ANSI_COLOR="0;31"
|
||||
CPE_NAME="cpe:/o:centos:centos:8"
|
||||
HOME_URL="https://centos.org/"
|
||||
BUG_REPORT_URL="https://bugs.centos.org/"
|
||||
CENTOS_MANTISBT_PROJECT="CentOS-8"
|
||||
CENTOS_MANTISBT_PROJECT_VERSION="8"
|
||||
(centos8) gilles@host:~$ id
|
||||
uid=1000(gilles) gid=1000(gilles) groups=1000(gilles)
|
||||
(centos8) gilles@host:~$ sudo id
|
||||
[sudo] password for gilles:
|
||||
uid=0(root) gid=0(root) groups=0(root)
|
||||
(centos8) gilles@host:~$ xclock
|
||||
$ runon ubuntu22.04
|
||||
(ubuntu22.04) gilles@host:~$ cat /etc/os-release
|
||||
PRETTY_NAME="Ubuntu 22.04.3 LTS"
|
||||
NAME="Ubuntu"
|
||||
VERSION_ID="22.04"
|
||||
VERSION="22.04.3 LTS (Jammy Jellyfish)"
|
||||
VERSION_CODENAME=jammy
|
||||
ID=ubuntu
|
||||
ID_LIKE=debian
|
||||
HOME_URL="https://www.ubuntu.com/"
|
||||
SUPPORT_URL="https://help.ubuntu.com/"
|
||||
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
|
||||
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
|
||||
UBUNTU_CODENAME=jammy
|
||||
(ubuntu22.04) gilles@host:~$ xclock
|
||||
^C
|
||||
(centos8) gilles@host:~$ exit
|
||||
(ubuntu22.04) gilles@host:~$ exit
|
||||
exit
|
||||
gilles@host:~$
|
||||
|
||||
To help differentiate the environment on are running on, you can add this
|
||||
snippet to your `.bashrc`:
|
||||
|
||||
# container
|
||||
if [ -n "$container" ]; then
|
||||
PS1="($container) $PS1"
|
||||
fi
|
||||
|
||||
This now displays your `runon` name on your bash prompt.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Configuration is done in `runon.conf` file, which describes supported
|
||||
distribution in .INI format.
|
||||
Configuration is done in `runon.yaml` file, which describes supported
|
||||
distribution in YAML format.
|
||||
|
||||
## Example config
|
||||
### Example config
|
||||
|
||||
```
|
||||
[DEFAULT]
|
||||
environment =
|
||||
HOME
|
||||
USER
|
||||
DISPLAY
|
||||
debian_chroot=${osname}
|
||||
rh_base: &rh_base
|
||||
dockerfile:
|
||||
- RUN dnf install -y sudo
|
||||
- RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
- RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
- RUN dnf group install -y "Development Tools"
|
||||
pkginstall:
|
||||
"RUN dnf install -y {}"
|
||||
packages:
|
||||
- xterm
|
||||
- vim-X11
|
||||
- git
|
||||
- python3
|
||||
- bash-completion
|
||||
binds:
|
||||
- /etc/timezone:ro
|
||||
- /etc/localtime:ro
|
||||
- "{home}"
|
||||
environment:
|
||||
- USER
|
||||
- DISPLAY
|
||||
- TERM
|
||||
- container={osname}
|
||||
|
||||
centos7:
|
||||
<<: *rh_base
|
||||
image: docker.io/centos:7
|
||||
dockerfile:
|
||||
- RUN yum install -y sudo
|
||||
- RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
- RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
- RUN yum group install -y "Development Tools"
|
||||
pkginstall:
|
||||
"RUN yum install -y {}"
|
||||
|
||||
binds =
|
||||
/etc/passwd:ro
|
||||
/etc/group:ro
|
||||
/etc/shadow:ro
|
||||
/tmp/.X11-unix:ro
|
||||
/home/${user}
|
||||
rocky8:
|
||||
<<: *rh_base
|
||||
image: docker.io/rockylinux:8
|
||||
|
||||
[centos8]
|
||||
dockerfile =
|
||||
FROM centos:8
|
||||
RUN yum install dnf-plugins-core -y
|
||||
RUN yum config-manager --set-enabled powertools -y
|
||||
RUN yum install sudo -y
|
||||
RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
RUN echo "ALL ALL=(ALL) ALL" >> /etc/sudoers
|
||||
pkginstall = RUN yum install {} -y
|
||||
packages = ksh csh xterm xorg-x11-apps xkeyboard-config git
|
||||
|
||||
[debian9]
|
||||
dockerfile =
|
||||
FROM debian:9
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install sudo
|
||||
RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
pkginstall = RUN apt-get -y install {}
|
||||
packages = ksh csh xterm x11-apps libgtk-3-0 build-essential git
|
||||
rocky9:
|
||||
<<: *rh_base
|
||||
image: docker.io/rockylinux:9
|
||||
```
|
||||
|
||||
Each section `[osname]` defines a distribution which can be used by runon.
|
||||
The `[DEFAULT]` section defines default values which is used if not
|
||||
overriden in individual section.
|
||||
Each entry which contains an `image:` field defines a distribution which can
|
||||
be used by runon. In the above example the other entries are used as templates
|
||||
for real entries.
|
||||
|
||||
## Config entries
|
||||
|
||||
### Config entries
|
||||
|
||||
* `image` the base image used to build the container.
|
||||
|
||||
* `dockerfile` the base content of dockerfile which will be used to
|
||||
generate the running environment. There is usually no need to diverge
|
||||
from the ones given in example.
|
||||
from the ones given in example.
|
||||
|
||||
* `pkginstall` the dockerfile command used to install a package, likely
|
||||
to be standard for all `deb` and `rpm` based distributions. In the
|
||||
@@ -192,26 +309,29 @@ overriden in individual section.
|
||||
|
||||
* `binds` the list of files and directories from the host system to
|
||||
expose in the container system. you might want to add `/opt` or other
|
||||
shared directories. See below for a description of `binds` entries
|
||||
shared directories. See below for a description of `binds` entries
|
||||
|
||||
* `environment` the list of environment variables you want to pass or
|
||||
set in the container system. See below for a description
|
||||
|
||||
Lines starting with `#` or `;` are comments.
|
||||
Lines starting with `#` or are comments.
|
||||
|
||||
Some substitution happens upon reading the configuration:
|
||||
|
||||
* `${user}` the current username
|
||||
* `${osname}` the executed distribution.
|
||||
* `{osname}` the executed distribution.
|
||||
* `{user}` the current username
|
||||
* `{uid}` the current UID
|
||||
* `{home}` the user's home directory
|
||||
|
||||
## Binds
|
||||
|
||||
### Binds
|
||||
|
||||
Each `binds` line can have one of the following formats:
|
||||
|
||||
<hostpath>
|
||||
<hostpath>:<mode>
|
||||
<hostpath>:<containerpath>
|
||||
<hostpath>:<containerpath>:<mode>
|
||||
<hostpath>:<mode>
|
||||
<hostpath>:<containerpath>
|
||||
<hostpath>:<containerpath>:<mode>
|
||||
|
||||
with:
|
||||
|
||||
@@ -221,7 +341,7 @@ with:
|
||||
it's the same path.
|
||||
* `<mode>` can be `rw`, read-write (by default), or `ro`, read-only.
|
||||
|
||||
## Environment
|
||||
### Environment
|
||||
|
||||
Each `environment` line define a Environment Variable which is set
|
||||
in the container upon execution.
|
||||
@@ -233,5 +353,3 @@ Each line can have one of the following formats:
|
||||
|
||||
If no value is given, the host value is passed into the container.
|
||||
|
||||
|
||||
|
||||
|
41
install
Executable file
41
install
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
srcdir=$(dirname $(readlink -f $0))
|
||||
venvdir=~/.local/lib/runon
|
||||
bindir=~/.local/bin
|
||||
configdir=~/.config/runon
|
||||
|
||||
if [ "$1" == "--dev" ]; then
|
||||
editable=--editable
|
||||
devmode=1
|
||||
fi
|
||||
|
||||
if [ ! -d $venvdir/bin/activate ]; then
|
||||
echo "create virtualenv $venvdir..."
|
||||
python3 -m venv $venvdir
|
||||
fi
|
||||
source $venvdir/bin/activate
|
||||
|
||||
echo "populate $venvdir..."
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install wheel
|
||||
python3 -m pip install $editable $srcdir
|
||||
|
||||
echo "create links in $bindir..."
|
||||
mkdir -p $bindir
|
||||
ln -sf $venvdir/bin/runon $bindir/
|
||||
|
||||
echo "install base config in $configdir..."
|
||||
mkdir -p $configdir
|
||||
if [ -n "$devmode" ]; then
|
||||
ln -s $srcdir/runon.default.yaml $configdir/
|
||||
else
|
||||
cp -p $srcdir/runon.default.yaml $configdir/
|
||||
fi
|
||||
|
||||
echo "done."
|
||||
|
||||
if [[ ":$PATH:" != *":$(readlink -f $bindir):"* ]]; then
|
||||
echo ""
|
||||
echo "WARNING: $bindir is not in your PATH"
|
||||
fi
|
177
runon
177
runon
@@ -1,177 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import docker
|
||||
import dockerpty
|
||||
import io
|
||||
import json
|
||||
import getpass
|
||||
import platform
|
||||
import os
|
||||
import re
|
||||
import xdg.BaseDirectory
|
||||
import configparser
|
||||
import subprocess
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
def load_config(user_confname, osname):
|
||||
ini_list = [ 'runon.conf', '.runon.conf', os.path.join(xdg.BaseDirectory.xdg_config_home, 'runon', 'runon.conf'), '/etc/runon/runon.conf' ]
|
||||
defaults = {
|
||||
'osname': osname,
|
||||
'user': getpass.getuser()
|
||||
}
|
||||
if user_confname:
|
||||
ini_list.insert(0, user_confname)
|
||||
ini = configparser.ConfigParser(defaults=defaults, interpolation=configparser.ExtendedInterpolation())
|
||||
ini.read(ini_list)
|
||||
if not ini.has_section(osname):
|
||||
print('ERROR: cannot find configuration for distribution "{}"'.format(osname))
|
||||
sys.exit(1)
|
||||
conf = {}
|
||||
fields = [ 'dockerfile', 'pkginstall', 'packages', 'environment', 'binds', 'user', 'osname' ]
|
||||
try:
|
||||
for f in fields:
|
||||
conf[f] = ini.get(osname, f)
|
||||
except configparser.NoOptionError as e:
|
||||
print('ERROR: {}'.format(e))
|
||||
sys.exit(1)
|
||||
for f in [ 'dockerfile', 'environment', 'binds' ]:
|
||||
conf[f] = [ i for i in conf[f].split('\n') if i ]
|
||||
for f in [ 'packages' ]:
|
||||
conf[f] = conf[f].split()
|
||||
return conf
|
||||
|
||||
|
||||
def make_image_name(osname):
|
||||
user = getpass.getuser()
|
||||
name = 'runon-{}-{}'.format(osname, user)
|
||||
return name
|
||||
|
||||
|
||||
def build_image(client, conf, update, verbose):
|
||||
packages = conf['packages']
|
||||
dockerfile = conf['dockerfile']
|
||||
pkginstall = conf['pkginstall']
|
||||
for p in packages:
|
||||
dockerfile.append(pkginstall.format(p))
|
||||
tag = make_image_name(conf['osname'])
|
||||
try:
|
||||
if verbose:
|
||||
# fallback to external command 'docker build' as there is
|
||||
# no way to follow the build progress with API.
|
||||
print('Building image {} ...'.format(tag))
|
||||
cmd = ['docker', 'build']
|
||||
if update:
|
||||
cmd.append('--no-cache')
|
||||
cmd += ['-t', tag, '-']
|
||||
ret = subprocess.run(cmd,
|
||||
input='\n'.join(dockerfile).encode('utf-8'),
|
||||
stderr=subprocess.STDOUT, check=True)
|
||||
image = client.images.get(tag)
|
||||
else:
|
||||
with io.BytesIO('\n'.join(dockerfile).encode('utf-8')) as fd:
|
||||
image, logs = client.images.build(tag=tag, fileobj=fd, rm=True, nocache=update)
|
||||
if verbose:
|
||||
print('Built image {} / {}'.format(image.tags[0], image.short_id))
|
||||
for l in logs:
|
||||
print(l.get('stream', '').strip('\n'))
|
||||
except (docker.errors.BuildError, KeyboardInterrupt, subprocess.CalledProcessError, docker.errors.ImageNotFound) as e:
|
||||
print('Build Error: {}'.format(e))
|
||||
print()
|
||||
print('with dockerfile:')
|
||||
for line in dockerfile:
|
||||
print(' {}'.format(line))
|
||||
sys.exit(1)
|
||||
return image
|
||||
|
||||
|
||||
def create_container(client, image, conf, command, verbose):
|
||||
volumes = {}
|
||||
environment = {}
|
||||
for mnt in conf['binds']:
|
||||
mnt = mnt.split(':')
|
||||
if mnt[-1] in ['ro','rw']:
|
||||
mode = mnt[-1]
|
||||
del mnt[-1]
|
||||
else:
|
||||
mode = 'rw'
|
||||
mnt = mnt[:2]
|
||||
bind = mnt[-1]
|
||||
vol = mnt[0]
|
||||
volumes[vol] = { 'bind': bind, 'mode': mode }
|
||||
hostname = platform.node()
|
||||
for v in conf['environment']:
|
||||
e = v.split('=')
|
||||
if len(e) == 1:
|
||||
e.append(os.getenv(e[0]))
|
||||
environment[e[0]] = e[1]
|
||||
#environment['debian_chroot']=conf['osname']
|
||||
user='{}:{}'.format(os.getuid(), os.getgid())
|
||||
pwd=os.getcwd()
|
||||
|
||||
container = client.containers.create(image, command,
|
||||
detach=False, stdin_open=True, tty=True,
|
||||
auto_remove=True,
|
||||
hostname=hostname,
|
||||
volumes=volumes,
|
||||
environment=environment,
|
||||
user=user,
|
||||
network_mode='host',
|
||||
working_dir=pwd
|
||||
)
|
||||
return container
|
||||
|
||||
|
||||
def run_container(client, container):
|
||||
dockerpty.start(client.api, container.id)
|
||||
container.reload() # to update attrs fields
|
||||
ret = container.attrs['State']['ExitCode']
|
||||
return ret
|
||||
|
||||
|
||||
def main():
|
||||
osname = None
|
||||
run_name = os.path.basename(os.sys.argv[0])
|
||||
if run_name == 'runos':
|
||||
pass
|
||||
elif run_name.startswith('runon'):
|
||||
m = re.match('runon[-_]?(.*)$', run_name)
|
||||
if m:
|
||||
osname = m[1]
|
||||
else:
|
||||
osname = run_name
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
if osname:
|
||||
parser.description = 'run commands on "{}" distribution'.format(osname)
|
||||
else:
|
||||
parser.description = 'run commands on any distribution'
|
||||
parser.add_argument('osname',
|
||||
help = 'distribution name to run on')
|
||||
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='verbose output')
|
||||
parser.add_argument('-c', '--config',
|
||||
help='specify config file')
|
||||
parser.add_argument('-u', '--update', action='store_true',
|
||||
help='force image update')
|
||||
parser.add_argument('command', nargs='*', default=None,
|
||||
help = 'command to execute')
|
||||
|
||||
args = parser.parse_args()
|
||||
if osname:
|
||||
args.osname = osname
|
||||
|
||||
client = docker.from_env()
|
||||
conf = load_config(args.config, args.osname)
|
||||
image = build_image(client, conf, args.update, args.verbose)
|
||||
container = create_container(client, image, conf, args.command, args.verbose)
|
||||
ret = run_container(client, container)
|
||||
return ret
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ret = main()
|
||||
sys.exit(ret)
|
53
runon.conf
53
runon.conf
@@ -1,53 +0,0 @@
|
||||
[DEFAULT]
|
||||
environment =
|
||||
HOME
|
||||
USER
|
||||
DISPLAY
|
||||
debian_chroot=${osname}
|
||||
|
||||
|
||||
binds =
|
||||
/etc/passwd:ro
|
||||
/etc/group:ro
|
||||
/etc/shadow:ro
|
||||
/tmp/.X11-unix:ro
|
||||
/home/${user}
|
||||
|
||||
[centos7]
|
||||
dockerfile =
|
||||
FROM centos:7
|
||||
RUN yum install sudo -y
|
||||
RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
RUN echo "ALL ALL=(ALL) ALL" >> /etc/sudoers
|
||||
pkginstall = RUN yum install {} -y
|
||||
packages = ksh csh xterm xorg-x11-apps xkeyboard-config git
|
||||
|
||||
[centos8]
|
||||
dockerfile =
|
||||
FROM centos:8
|
||||
RUN yum install dnf-plugins-core -y
|
||||
RUN yum config-manager --set-enabled powertools -y
|
||||
RUN yum install sudo -y
|
||||
RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
RUN echo "ALL ALL=(ALL) ALL" >> /etc/sudoers
|
||||
pkginstall = RUN yum install {} -y
|
||||
packages = ksh csh xterm xorg-x11-apps xkeyboard-config git
|
||||
|
||||
[debian9]
|
||||
dockerfile =
|
||||
FROM debian:9
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install sudo
|
||||
RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
pkginstall = RUN apt-get -y install {}
|
||||
packages = ksh csh xterm x11-apps libgtk-3-0 build-essential git
|
||||
|
||||
[ubuntu20.04]
|
||||
dockerfile =
|
||||
FROM ubuntu:20.04
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install sudo
|
||||
pkginstall = RUN apt-get -y install {}
|
||||
packages = ksh csh xterm x11-apps build-essential git
|
||||
|
||||
|
107
runon.default.yaml
Normal file
107
runon.default.yaml
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
debian_base: &debian_base
|
||||
dockerfile:
|
||||
- ARG DEBIAN_FRONTEND=noninteractive
|
||||
- RUN apt-get update
|
||||
- RUN apt-get -y install apt-utils
|
||||
- RUN apt-get -y upgrade
|
||||
- RUN apt-get -y install sudo
|
||||
- RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
- RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
pkginstall:
|
||||
"RUN apt-get -y install {}"
|
||||
packages:
|
||||
- xterm
|
||||
- x11-apps
|
||||
- vim-gtk3
|
||||
- git
|
||||
- build-essential
|
||||
- python3
|
||||
- bash-completion
|
||||
binds:
|
||||
- /etc/timezone:ro
|
||||
- /etc/localtime:ro
|
||||
- "{home}"
|
||||
environment:
|
||||
- USER
|
||||
- DISPLAY
|
||||
- TERM
|
||||
- container={osname}
|
||||
|
||||
debian9:
|
||||
<<: *debian_base
|
||||
image: docker.io/debian:9
|
||||
dockerfile:
|
||||
- ARG DEBIAN_FRONTEND=noninteractive
|
||||
- RUN echo "deb http://archive.debian.org/debian stretch main" > /etc/apt/sources.list
|
||||
- RUN apt-get update
|
||||
- RUN apt-get -y upgrade
|
||||
- RUN apt install -y --allow-downgrades libnettle6=3.3-1+b2 # default libnettle6 conflicts with libgtk-3.0
|
||||
- RUN apt-get -y install sudo
|
||||
- RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
- RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
|
||||
debian10:
|
||||
<<: *debian_base
|
||||
image: docker.io/debian:10
|
||||
|
||||
debian11:
|
||||
<<: *debian_base
|
||||
image: docker.io/debian:11
|
||||
|
||||
debian12:
|
||||
<<: *debian_base
|
||||
image: docker.io/debian:12
|
||||
|
||||
ubuntu20.04:
|
||||
<<: *debian_base
|
||||
image: docker.io/ubuntu:20.04
|
||||
|
||||
ubuntu22.04:
|
||||
<<: *debian_base
|
||||
image: docker.io/ubuntu:22.04
|
||||
|
||||
rh_base: &rh_base
|
||||
dockerfile:
|
||||
- RUN dnf install -y sudo
|
||||
- RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
- RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
- RUN dnf group install -y "Development Tools"
|
||||
pkginstall:
|
||||
"RUN dnf install -y {}"
|
||||
packages:
|
||||
- xterm
|
||||
- vim-X11
|
||||
- git
|
||||
- python3
|
||||
- bash-completion
|
||||
binds:
|
||||
- /etc/timezone:ro
|
||||
- /etc/localtime:ro
|
||||
- "{home}"
|
||||
environment:
|
||||
- USER
|
||||
- DISPLAY
|
||||
- TERM
|
||||
- container={osname}
|
||||
|
||||
|
||||
centos7:
|
||||
<<: *rh_base
|
||||
image: docker.io/centos:7
|
||||
dockerfile:
|
||||
- RUN yum install -y sudo
|
||||
- RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||
- RUN echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
- RUN yum group install -y "Development Tools"
|
||||
pkginstall:
|
||||
"RUN yum install -y {}"
|
||||
|
||||
rocky8:
|
||||
<<: *rh_base
|
||||
image: docker.io/rockylinux:8
|
||||
|
||||
rocky9:
|
||||
<<: *rh_base
|
||||
image: docker.io/rockylinux:9
|
||||
|
0
runon/__init__.py
Normal file
0
runon/__init__.py
Normal file
230
runon/runon.py
Executable file
230
runon/runon.py
Executable file
@@ -0,0 +1,230 @@
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import getpass
|
||||
import pathlib
|
||||
import xdg.BaseDirectory
|
||||
import yaml
|
||||
import datetime
|
||||
import pytz
|
||||
import subprocess
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
def find_config_file(user_conf):
|
||||
conf_list = [
|
||||
'runon.yaml',
|
||||
'.runon.yaml',
|
||||
os.path.join(xdg.BaseDirectory.xdg_config_home, 'runon', 'runon.yaml'),
|
||||
os.path.join(xdg.BaseDirectory.xdg_config_home, 'runon', 'runon.default.yaml')
|
||||
]
|
||||
if user_conf:
|
||||
conf_list = [ user_conf ]
|
||||
for conf in conf_list:
|
||||
if os.path.exists(conf):
|
||||
return conf
|
||||
return None
|
||||
|
||||
|
||||
def read_yaml(conf_file):
|
||||
try:
|
||||
with open(conf_file, 'r') as file:
|
||||
conf = yaml.safe_load(file)
|
||||
conf['stamp'] = datetime.datetime.fromtimestamp(os.path.getmtime(conf_file), tz=pytz.UTC)
|
||||
return conf
|
||||
except yaml.YAMLError as e:
|
||||
print(f'ERROR: bad configuration file:')
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
return conf
|
||||
|
||||
def list_osnames(conf_file):
|
||||
conf = read_yaml(conf_file)
|
||||
osnames = []
|
||||
for key in conf:
|
||||
if (type(conf[key]) is dict) and conf[key].get('image'):
|
||||
osnames.append(key)
|
||||
return osnames
|
||||
|
||||
|
||||
def load_config(conf_file, osname):
|
||||
user_vars = {
|
||||
'osname': osname,
|
||||
'user': getpass.getuser(),
|
||||
'uid': os.getuid(),
|
||||
'home': pathlib.Path.home(),
|
||||
}
|
||||
conf = read_yaml(conf_file)
|
||||
osconf = conf.get(osname)
|
||||
if not osconf:
|
||||
print(f"ERROR: cannot find configuration for distribution {osname}")
|
||||
sys.exit(1)
|
||||
|
||||
osconf['stamp'] = conf.get('stamp')
|
||||
osconf['osname'] = osname
|
||||
for k in [ 'dockerfile', 'packages', 'environment', 'binds' ]:
|
||||
if osconf.get(k):
|
||||
osconf[k] = [ s.format(**user_vars) for s in osconf[k]]
|
||||
return osconf
|
||||
|
||||
|
||||
def make_osname_link(binpath, osname):
|
||||
link = os.path.join(os.path.dirname(binpath), osname)
|
||||
try:
|
||||
os.symlink('runon', link)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
|
||||
def build_image(conf, update, verbose):
|
||||
osname = conf.get('osname')
|
||||
image_name = 'runon-{}'.format(osname)
|
||||
cache_dir = os.path.join(xdg.BaseDirectory.xdg_cache_home, 'runon')
|
||||
cache_file = os.path.join(cache_dir, image_name)
|
||||
|
||||
if not update and os.path.exists(cache_file):
|
||||
ts_image = datetime.datetime.fromtimestamp(os.path.getmtime(cache_file), tz=pytz.UTC)
|
||||
ts_conf = conf.get('stamp')
|
||||
if verbose:
|
||||
print('config: {}'.format(ts_conf))
|
||||
print('image: {}'.format(ts_image))
|
||||
if ts_image and ts_image > ts_conf:
|
||||
if verbose:
|
||||
print('image: {} up-to-date'.format(image_name))
|
||||
return image_name
|
||||
|
||||
image = conf.get('image')
|
||||
dockerfile = conf.get('dockerfile')
|
||||
pkginstall = conf.get('pkginstall')
|
||||
packages = conf.get('packages')
|
||||
if not dockerfile:
|
||||
dockerfile = []
|
||||
if image:
|
||||
dockerfile.insert(0, 'FROM {}'.format(image))
|
||||
if packages:
|
||||
for p in packages:
|
||||
dockerfile.append(pkginstall.format(p))
|
||||
|
||||
if verbose:
|
||||
print('Dockerfile:')
|
||||
for l in dockerfile:
|
||||
print(' * {}'.format(l))
|
||||
print('Building image {} ...'.format(image_name))
|
||||
cmd = ['podman', 'build']
|
||||
if update:
|
||||
cmd.append('--no-cache')
|
||||
cmd += ['-t', image_name, '-']
|
||||
ret = subprocess.run(cmd,
|
||||
input='\n'.join(dockerfile).encode('utf-8'),
|
||||
stderr=subprocess.STDOUT, check=True)
|
||||
|
||||
if not os.path.exists(cache_dir):
|
||||
os.mkdir(cache_dir)
|
||||
with open(cache_file, 'w') as file:
|
||||
if verbose:
|
||||
print('cache: {}'.format(cache_file))
|
||||
file.write('')
|
||||
|
||||
return image_name
|
||||
|
||||
|
||||
def run_image(name, conf, command, verbose):
|
||||
volumes = {
|
||||
}
|
||||
environment = {}
|
||||
if conf.get('binds'):
|
||||
for mnt in conf['binds']:
|
||||
mnt = mnt.split(':')
|
||||
if mnt[-1] in ['ro','rw']:
|
||||
mode = mnt[-1]
|
||||
del mnt[-1]
|
||||
else:
|
||||
mode = 'rw'
|
||||
mnt = mnt[:2]
|
||||
bind = mnt[-1]
|
||||
vol = mnt[0]
|
||||
if os.path.exists(vol):
|
||||
volumes[vol] = { 'bind': bind, 'mode': mode }
|
||||
if conf.get('environment'):
|
||||
for v in conf['environment']:
|
||||
e = v.split('=')
|
||||
if len(e) == 1:
|
||||
e.append(os.getenv(e[0]))
|
||||
environment[e[0]] = e[1]
|
||||
|
||||
cmd = ['podman', 'run', '--rm', '--interactive', '--tty', '--userns=keep-id', '--net=host' ]
|
||||
for e in environment:
|
||||
cmd += [ '-e', '{}={}'.format(e, environment[e]) ]
|
||||
for v in volumes:
|
||||
cmd += [ '-v', ':'.join([v, volumes[v]['bind'], volumes[v]['mode']]) ]
|
||||
cmd += [ '--workdir', os.getcwd() ]
|
||||
cmd += [ name ]
|
||||
if command:
|
||||
cmd += command
|
||||
if verbose:
|
||||
print('executing: {}\n'.format(' '.join(cmd)))
|
||||
ret = subprocess.run(cmd)
|
||||
return ret.returncode
|
||||
|
||||
|
||||
def main():
|
||||
osname = None
|
||||
run_name = os.path.basename(os.sys.argv[0])
|
||||
if run_name == "runon":
|
||||
pass
|
||||
elif run_name.startswith("runon_"):
|
||||
osname = run_name[len("runon_"):]
|
||||
else:
|
||||
osname = run_name
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
if osname:
|
||||
parser.description = 'run commands on "{}" distribution'.format(osname)
|
||||
else:
|
||||
parser.description = 'run commands on any distribution'
|
||||
parser.add_argument('osname',
|
||||
help = 'distribution name to run on, '
|
||||
'"list" to dump all available distributions, '
|
||||
'"edit" to open the current config file in a text editor.')
|
||||
parser.epilog = '(c) 2021 Gilles Grandou <gilles@grandou.net>'
|
||||
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='verbose output')
|
||||
parser.add_argument('-c', '--config',
|
||||
help='specify config file')
|
||||
parser.add_argument('-u', '--update', action='store_true',
|
||||
help='force image update')
|
||||
parser.add_argument('-l', '--link', action='store_true',
|
||||
help='create a symlink to call "osname" as a shortcut to "runon osname"')
|
||||
parser.add_argument('command', nargs='*', default=None,
|
||||
help = 'command to execute')
|
||||
|
||||
args = parser.parse_args()
|
||||
if osname:
|
||||
args.osname = osname
|
||||
|
||||
conf_file = find_config_file(args.config)
|
||||
if not conf_file:
|
||||
print('ERROR: config file not found')
|
||||
sys.exit(1)
|
||||
|
||||
if args.osname == 'list':
|
||||
osnames = list_osnames(conf_file)
|
||||
print('Available distributions:')
|
||||
for o in sorted(osnames):
|
||||
print(' {}'.format(o))
|
||||
print()
|
||||
return 0
|
||||
elif args.osname == 'edit':
|
||||
cmd = [ 'xdg-open', conf_file ]
|
||||
ret = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL);
|
||||
return 0
|
||||
|
||||
if args.link:
|
||||
make_osname_link(sys.argv[0], args.osname)
|
||||
|
||||
conf = load_config(conf_file, args.osname)
|
||||
image_name = build_image(conf, args.update, args.verbose)
|
||||
ret = run_image(image_name, conf, args.command, args.verbose)
|
||||
return ret
|
||||
|
22
setup.py
Normal file
22
setup.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name = "runon",
|
||||
version = "2.0.0.dev0",
|
||||
maintainer = "Gilles Grandou",
|
||||
maintainer_email = "gilles@grandou.net",
|
||||
description = "Run your commands on any systems",
|
||||
packages = find_packages(),
|
||||
python_requires = ">=3.6",
|
||||
install_requires = [
|
||||
"pathlib",
|
||||
"pyxdg",
|
||||
"pyyaml",
|
||||
"pytz",
|
||||
],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"runon=runon.runon:main",
|
||||
],
|
||||
}
|
||||
)
|
16
tests.sh
Executable file
16
tests.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
cat /etc/os-release
|
||||
echo
|
||||
|
||||
set -x
|
||||
sudo id
|
||||
python3 --version
|
||||
g++ --version | head -1
|
||||
gvim --version | head -1
|
||||
xterm -e /bin/bash -c "sleep 1"
|
||||
date
|
||||
set +x
|
||||
|
||||
echo "[OK]"
|
||||
|
25
uninstall
Executable file
25
uninstall
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
srcdir=$(dirname $(readlink -f $0))
|
||||
venvdir=~/.local/lib/runon
|
||||
bindir=~/.local/bin
|
||||
configdir=~/.config/runon
|
||||
|
||||
if [ -e $venvdir/bin/runon ]; then
|
||||
echo "remove links from $bindir"
|
||||
find -L $bindir -samefile $bindir/runon -exec rm -v {} \;
|
||||
find -L $bindir -samefile $venvdir/bin/runon -exec rm -v {} \;
|
||||
fi
|
||||
|
||||
if [ -d $venvdir ]; then
|
||||
echo "remove virtualenv $venvdir"
|
||||
rm -rf $venvdir
|
||||
fi
|
||||
|
||||
if [ -e $configdir ]; then
|
||||
echo "remove configs"
|
||||
rm -vf $configdir/runon.default.yaml
|
||||
rmdir -v $configdir || true
|
||||
fi
|
||||
|
||||
echo "done."
|
Reference in New Issue
Block a user