Skip to content
This repository has been archived by the owner on Oct 2, 2024. It is now read-only.

Commit

Permalink
PR #338 from @j-ogas and me: add Fedora/EPEL compliant spec file (closes
Browse files Browse the repository at this point in the history
  • Loading branch information
j-ogas authored and reidpr committed Mar 7, 2019
1 parent 6929d30 commit e67d290
Show file tree
Hide file tree
Showing 19 changed files with 583 additions and 166 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.gitattributes export-ignore
.gitignore export-ignore
.gitmodules export-ignore
68 changes: 38 additions & 30 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
SHELL=/bin/bash

# Add some good stuff to CFLAGS.
export CFLAGS += -std=c11 -Wall
export CFLAGS += -std=c11 -Wall -g

.PHONY: all
all: VERSION.full bin/version.h bin/version.sh
Expand All @@ -16,22 +16,18 @@ clean:
cd test && $(MAKE) clean
cd examples/syscalls && $(MAKE) clean

# VERSION.full contains the version string reported by the executables.
#
# * If VERSION is an unadorned release (e.g. 0.2.3 not 0.2.3~pre), or there's
# no Git information available, VERSION.full is simply a copy of VERSION.
#
# * Otherwise, we add the Git branch if the current branch is not master, the
# Git commit, and a note if the working directory
# contains uncommitted changes, e.g. "0.2.3~pre+experimental.ae24a4e.dirty".
# VERSION.full contains the version string reported by executables; see FAQ.
ifeq ($(shell test -d .git && fgrep -q \~ VERSION && echo true),true)
.PHONY: VERSION.full # depends on git metadata, not a simple file
VERSION.full: VERSION
(git --version > /dev/null 2>&1) || \
(echo "This is a Git working directory but no git found." && false)
printf '%s+%s%s%s\n' \
$$(cat $<) \
$$(git rev-parse --abbrev-ref HEAD | sed 's/.*/&./g' | sed 's/master.//g') \
$$( git rev-parse --abbrev-ref HEAD \
| sed 's/[^A-Za-z0-9]//g' \
| sed 's/$$/./g' \
| sed 's/master.//g') \
$$(git rev-parse --short HEAD) \
$$(git diff-index --quiet HEAD || echo '.dirty') \
> $@
Expand All @@ -44,25 +40,37 @@ bin/version.h: VERSION.full
bin/version.sh: VERSION.full
echo "version () { echo 1>&2 '$$(cat $<)'; }" > $@

# Yes, this is bonkers. We keep it around even though normal "git archive" or
# the zip files on Github work, because it provides an easy way to create a
# self-contained tarball with embedded Bats and man pages.
# These targets provide tarballs of HEAD (not the Git working directory) that
# are self-contained, including the source code as well as the man pages
# (both) and Bats (export-bats). To use them in an unclean working directory,
# set $CH_UNCLEAN_EXPORT_OK to non-empty.
#
# You must "cd doc-src && make" before this will work.
.PHONY: export
export: VERSION.full man/charliecloud.1
test -d .git -a -f test/bats/.git # need recursive Git checkout
# git diff-index --quiet HEAD # need clean working directory
# You must "cd doc-src && make" before they will work. The targets depend on
# the man pages but don't know how to build them.
#
# They are phony because I haven't figured out their real dependencies.
.PHONY: main.tar
main.tar: VERSION.full man/charliecloud.1
git diff-index --quiet HEAD || [ -n "$$CH_MAKE_EXPORT_UNCLEAN_OK" ]
git archive HEAD --prefix=charliecloud-$$(cat VERSION.full)/ \
-o main.tar
tar --xform=s,^,charliecloud-$$(cat VERSION.full)/, \
-rf main.tar man/*.1 VERSION.full

.PHONY: export
export: main.tar
gzip -9 main.tar
mv main.tar.gz charliecloud-$$(cat VERSION.full).tar.gz
ls -lh charliecloud-$$(cat VERSION.full).tar.gz

.PHONY: export-bats
export-bats: main.tar
test -d .git -a -f test/bats/.git # need recursive Git checkout
cd test/bats && \
git archive HEAD \
--prefix=charliecloud-$$(cat ../../VERSION.full)/test/bats/ \
-o ../../bats.tar
tar Af main.tar bats.tar
tar --xform=s,^,charliecloud-$$(cat VERSION.full)/, \
-rf main.tar \
man/*.1 VERSION.full
gzip -9 main.tar
mv main.tar.gz charliecloud-$$(cat VERSION.full).tar.gz
rm bats.tar
Expand Down Expand Up @@ -95,14 +103,14 @@ endif
INSTALL_PREFIX := $(if $(DESTDIR),$(DESTDIR)/$(PREFIX),$(PREFIX))
BIN := $(INSTALL_PREFIX)/bin
DOC := $(INSTALL_PREFIX)/share/doc/charliecloud
TEST := $(DOC)/test
# LIBEXEC_DIR is modeled after FHS 3.0 and
# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html. It
# contains any executable helpers that are not needed in PATH. Default is
# libexec/charliecloud which will be preprended with the PREFIX.
LIBEXEC_DIR ?= libexec/charliecloud
LIBEXEC_INST := $(INSTALL_PREFIX)/$(LIBEXEC_DIR)
LIBEXEC_RUN := $(PREFIX)/$(LIBEXEC_DIR)
TEST := $(LIBEXEC_INST)/test
.PHONY: install
install: all
@test -n "$(PREFIX)" || \
Expand All @@ -129,14 +137,14 @@ install: all
install -pm 644 -t $(DOC) LICENSE README.rst
# examples
for i in examples/syscalls examples/{serial,mpi,other}/*; do \
install -d $(DOC)/$$i; \
install -pm 644 -t $(DOC)/$$i $$i/*; \
install -d $(LIBEXEC_INST)/$$i; \
install -pm 644 -t $(LIBEXEC_INST)/$$i $$i/*; \
done
chmod 755 $(DOC)/examples/serial/hello/hello.sh \
$(DOC)/examples/syscalls/pivot_root \
$(DOC)/examples/syscalls/userns \
$(DOC)/examples/*/*/*.sh
find $(DOC)/examples -name Build -exec chmod 755 {} \;
chmod 755 $(LIBEXEC_INST)/examples/serial/hello/hello.sh \
$(LIBEXEC_INST)/examples/syscalls/pivot_root \
$(LIBEXEC_INST)/examples/syscalls/userns \
$(LIBEXEC_INST)/examples/*/*/*.sh
find $(LIBEXEC_INST)/examples -name Build -exec chmod 755 {} \;
# tests
install -d $(TEST) $(TEST)/run
install -pm 644 -t $(TEST) test/*.bats test/common.bash test/Makefile
Expand All @@ -148,7 +156,7 @@ install: all
install -d $(TEST)/chtest
install -pm 644 -t $(TEST)/chtest test/chtest/*
chmod 755 $(TEST)/chtest/{Build,*.py,printns}
ln -sf ../../../../bin $(TEST)/bin
ln -sf ../../../bin $(TEST)/bin
# shared library tests
install -d $(TEST)/sotest $(TEST)/sotest/bin $(TEST)/sotest/lib
install -pm 755 -t $(TEST)/sotest test/sotest/libsotest.so.1.0 \
Expand Down
5 changes: 5 additions & 0 deletions bin/ch-run.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const struct argp_option options[] = {
{ "bind", 'b', "SRC[:DST]", 0,
"mount SRC at guest DST (default /mnt/0, /mnt/1, etc.)"},
{ "cd", 'c', "DIR", 0, "initial working directory in container"},
{ "ch-ssh", -8, 0, 0, "bind ch-ssh into image"},
{ "gid", 'g', "GID", 0, "run as GID within container" },
{ "join", 'j', 0, 0, "use same container as peer ch-run" },
{ "join-pid", -5, "PID", 0, "join a namespace using a PID" },
Expand Down Expand Up @@ -106,6 +107,7 @@ int main(int argc, char *argv[])
privs_verify_invoking();

T_ (args.c.binds = calloc(1, sizeof(struct bind)));
args.c.ch_ssh = false;
args.c.container_gid = getegid();
args.c.container_uid = geteuid();
args.c.join = false;
Expand Down Expand Up @@ -384,6 +386,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
Te (strlen(arg) > 0, "--unset-env: GLOB must have non-zero length");
env_delta_append(&(args->env_deltas), UNSET_GLOB, arg);
break;;
case -8: // --ch-ssh
args->c.ch_ssh = true;
break;
case 'c':
args->initial_dir = arg;
break;
Expand Down
6 changes: 4 additions & 2 deletions bin/charliecloud.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,13 @@ void enter_udss(struct container *c)
Z_ (mkdir(cat(c->newroot, newhome), 0755));
bind_mount(c->old_home, newhome, c->newroot, BD_REQUIRED, 0);
}
// Bind-mount /usr/bin/ch-ssh if it exists.
if (path_exists(cat(c->newroot, "/usr/bin/ch-ssh"))) {
// Container /usr/bin/ch-ssh.
if (c->ch_ssh) {
char chrun_file[PATH_CHARS];
int len = readlink("/proc/self/exe", chrun_file, PATH_CHARS);
T_ (len >= 0);
Te (path_exists(cat(c->newroot, "/usr/bin/ch-ssh")),
"--ch-ssh: /usr/bin/ch-ssh not in image");
chrun_file[ len<PATH_CHARS ? len : PATH_CHARS-1 ] = 0; // terminate; #315
bind_mount(cat(dirname(chrun_file), "/ch-ssh"), "/usr/bin/ch-ssh",
c->newroot, BD_REQUIRED, 0);
Expand Down
1 change: 1 addition & 0 deletions bin/charliecloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ enum bind_dep {

struct container {
struct bind *binds;
bool ch_ssh; // bind /usr/bin/ch-ssh?
gid_t container_gid;
uid_t container_uid;
char *newroot;
Expand Down
3 changes: 3 additions & 0 deletions doc-src/ch-run_desc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ unpacked image directory located at :code:`NEWROOT`.
:code:`-c`, :code:`--cd=DIR`
initial working directory in container

:code:`--ch-ssh`
bind :code:`ch-ssh(1)` into container at :code:`/usr/bin/ch-ssh`

:code:`-g`, :code:`--gid=GID`
run as group :code:`GID` within container

Expand Down
104 changes: 104 additions & 0 deletions doc-src/dev.rst
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,110 @@ suite.
* else: An error occurred.


Building RPMs
=============

We maintain :code:`.spec` files and infrastructure for building RPMs in the
Charliecloud source code. This is for two purposes:

1. We maintain our own Fedora RPMs.
2. We want to be able to build an RPM of any commit.

Item 2 is tested; i.e., if you break the RPM build, the test suite will fail.

This section describes how to build the RPMs and the pain we've hopefully
abstracted away.

Prerequisites
-------------

* Python 2.7
* Either:

* RPM-based system of roughly RHEL/CentOS 7 vintage or newer, with RPM
build tools installed
* System that can run Charliecloud containers

:code:`rpmbuild` wrapper script
-------------------------------

While building the Charliecloud RPMs is not too weird, we provide a script to
streamline it. The purpose is to (a) make it easy to build versions not
matching the working directory, (b) use an arbitrary :code:`rpmbuild`
directory, and (c) build in a Charliecloud container for non-RPM-based
environments.

The script must be run from the root of a Charliecloud Git working directory.

Usage::

$ packaging/fedora/build [OPTIONS] VERSION

Options:

* :code:`--image=DIR` : Build in Charliecloud image directory :code:`DIR`.

* :code:`--install` : Install the RPMs after building into the build
environment.

* :code:`--rpmbuild=DIR` : Use RPM build directory root :code:`DIR`
(default: :code:`~/rpmbuild`).

For example, to build a version 0.9.7 RPM, on an RPM system, and leave the
results in :code:`~/rpmbuild/RPMS`::

$ packaging/fedora/build 0.9.7-1

To build a pre-release RPM of Git HEAD using the CentOS 7 image provided with
the test suite (note that the test suite would also build the necessary image
directory)::

$ bin/ch-build -t centos7 -f test/Dockerfile.centos7 test
$ bin/ch-docker2tar centos7 $CH_TEST_TARDIR
$ bin/ch-tar2dir $CH_TEST_TARDIR/centos7.tar.gz $CH_TEST_IMGDIR
$ packaging/fedora/build --image $CH_TEST_IMGDIR/centos7 HEAD

Gotchas and quirks
------------------

RPM versions and releases
~~~~~~~~~~~~~~~~~~~~~~~~~

If :code:`VERSION` is :code:`HEAD`, then the RPM version will be the content
of :code:`VERSION.full` for that commit, including Git gobbledygook, and the
RPM release will be :code:`0`. Note that such RPMs cannot be reliably upgraded
because their version numbers are unordered.

Otherwise, :code:`VERSION` should be a released Charliecloud version followed
by a hyphen and the desired RPM release, e.g. :code:`0.9.7-3`.

Other values of :code:`VERSION` (e.g., a branch name) may work but are not
supported.

Packaged source code and RPM build config come from different commits
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The spec file, :code:`build` script, :code:`.rpmlintrc`, etc. come from the
working directory, but the package source is from the specified commit. This
is what enables us to make additional RPM releases for a given Charliecloud
release (e.g. 0.9.7-2).

Corollaries of this policy are that RPM build configuration can be any or no
commit, and it's not possible to create an RPM of uncommitted source code.

Changelog maintenance
~~~~~~~~~~~~~~~~~~~~~

The spec file changelog contains manually maintained release notes for all
Charliecloud released versions and corresponding RPM releases. For released
versions, :code:`build` verifies that the most recent changelog entry matches
the given :code:`VERSION` argument. The timestamp is not automatically
verified.

For other Charliecloud versions, :code:`build` adds a generic changelog entry
with the appropriate version stating that it's a pre-release RPM.


Coding style
============

Expand Down
40 changes: 40 additions & 0 deletions doc-src/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,46 @@ two solutions:
Unexpected behavior
===================

What do the version numbers mean?
---------------------------------

Released versions of Charliecloud have a pretty standard version number, e.g.
0.9.7.

Work leading up to a released version also has version numbers, to satisfy
tools that require them and to give the executables something useful to report
on :code:`--version`, but these can be quite messy. We refer to such versions
informally as *pre-releases*, but Charliecloud does not have formal
pre-releases such as alpha, beta, or release candidate.

*Pre-release version numbers are not in order*, because this work is in a DAG
rather than linear, except they precede the version we are working towards. If
you're dealing with these versions, use Git.

Pre-release version numbers are the version we are working towards, followed
by: :code:`~pre`, the branch name if not :code:`master` with non-alphanumerics
removed, the commit hash, and finally :code:`dirty` if the working directory
had uncommitted changes.

Examples:

* :code:`0.2.0` : Version 0.2.0. Released versions don't include Git
information, even if built in a Git working directory.

* :code:`0.2.1~pre` : Some snapshot of work leading up to 0.2.1, built from
source code where the Git information has been lost, e.g. the tarballs
Github provides. This should make you wary because you don't have any
provenance. It might even be uncommitted work or an abandoned branch.

* :code:`0.2.1~pre.1a99f42` : Master branch commit 1a99f42, built from a
clean working directory (i.e., no changes since that commit).

* :code:`0.2.1~pre.foo1.0729a78` : Commit 0729a78 on branch :code:`foo-1`,
:code:`foo_1`, etc. built from clean working directory.

* :code:`0.2.1~pre.foo1.0729a78.dirty` : Commit 0729a78 on one of those
branches, plus un-committed changes.

:code:`--uid 0` lets me read files I can’t otherwise!
-----------------------------------------------------

Expand Down
Loading

0 comments on commit e67d290

Please sign in to comment.