├── .copr ├── Makefile └── atomic.spec ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .papr.Dockerfile ├── .papr.sh ├── .papr.yml ├── Atomic ├── Export.py ├── Import.py ├── __init__.py ├── atomic.py ├── backends │ ├── __init__.py │ ├── _containers_storage.py │ ├── _docker.py │ ├── _docker_errors.py │ ├── _ostree.py │ └── backend.py ├── backendutils.py ├── client.py ├── containers.py ├── delete.py ├── diff.py ├── discovery.py ├── help.py ├── host.py ├── images.py ├── info.py ├── install.py ├── mount.py ├── objects │ ├── __init__.py │ ├── container.py │ ├── image.py │ └── layer.py ├── pull.py ├── pulp.py ├── push.py ├── rpm_host_install.py ├── rpmwriter.py ├── run.py ├── satellite.py ├── satellite_new.py.test ├── scan.py ├── sign.py ├── stop.py ├── storage.py ├── syscontainers.py ├── tag.py ├── top.py ├── trust.py ├── uninstall.py ├── update.py ├── util.py └── verify.py ├── CHANGELOG.md ├── CONTRIBUTING.md ├── COPYING ├── LICENSE ├── Makefile ├── README-atomic-scan.md ├── README.md ├── Vagrantfile ├── atomic ├── atomic-containers.1.md ├── atomic.conf ├── atomic.d └── openscap ├── atomic.sh ├── atomic.sysconfig ├── atomic_dbus.py ├── atomic_dbus_client.py ├── atomicdesign.pdf ├── bash └── atomic ├── docs ├── .gitignore ├── atomic-containers.1.md ├── atomic-diff.1.md ├── atomic-help.1.md ├── atomic-host.1.md ├── atomic-images.1.md ├── atomic-install.1.md ├── atomic-mount.1.md ├── atomic-pull.1.md ├── atomic-push.1.md ├── atomic-run.1.md ├── atomic-scan.1.md ├── atomic-sign.1.md ├── atomic-stop.1.md ├── atomic-storage.1.md ├── atomic-top.1.md ├── atomic-trust.1.md ├── atomic-uninstall.1.md ├── atomic-unmount.1.md ├── atomic.1.md └── install │ ├── Debian.md │ └── Fedora.md ├── gotar.go ├── migrate.sh ├── org.atomic.conf ├── org.atomic.policy ├── org.atomic.service ├── requirements.txt ├── setup.py ├── test.sh ├── tests ├── atomic-client.js ├── atomicClient.html ├── integration │ ├── _test_install.sh │ ├── setup-scripts │ │ └── system_containers_setup.sh │ ├── test_containers_list.sh │ ├── test_dbus.py │ ├── test_dbus_client.py │ ├── test_diff.sh │ ├── test_display.sh │ ├── test_help.sh │ ├── test_images_list.sh │ ├── test_info.sh │ ├── test_migrate.sh │ ├── test_mount.sh │ ├── test_params.sh │ ├── test_pass.sh │ ├── test_printenv.sh │ ├── test_run.sh │ ├── test_run_opts.sh │ ├── test_storage.sh │ ├── test_system_containers_images.sh │ ├── test_system_containers_install.sh │ ├── test_system_containers_mount.sh │ ├── test_system_containers_pull.sh │ ├── test_system_containers_rpm.sh │ ├── test_system_containers_runtime.sh │ ├── test_tag.sh │ ├── test_top.sh │ └── test_verify.sh ├── integration_template.sh ├── manifest.json ├── test-images │ ├── Dockerfile.1 │ ├── Dockerfile.3 │ ├── Dockerfile.4 │ ├── Dockerfile.5 │ ├── Dockerfile.6 │ ├── Dockerfile.runonce │ ├── Dockerfile.system │ ├── Dockerfile.system-hostfs │ ├── Dockerfile.system-update │ ├── help.1 │ ├── help.sh │ ├── install.sh │ ├── show-hostname.sh │ ├── system-container-files-hostfs │ │ ├── config.json.template │ │ ├── greet.sh │ │ ├── manifest.json │ │ ├── message │ │ ├── message-template │ │ ├── run.sh │ │ ├── service.template │ │ └── tmpfiles.template │ ├── system-container-files │ │ ├── config.json.template │ │ ├── greet.sh │ │ ├── manifest.json │ │ ├── run.sh │ │ ├── service.template │ │ └── tmpfiles.template │ ├── system-container-runonce-files │ │ ├── config.json.template │ │ ├── hi.sh │ │ └── manifest.json │ └── system-container-update-files │ │ ├── config.json.template │ │ ├── greet.sh │ │ ├── manifest.json │ │ ├── run.sh │ │ ├── service.template │ │ └── tmpfiles.template ├── unit │ ├── fixtures │ │ ├── atomic.conf │ │ ├── configs │ │ │ ├── docker.io-repo.yaml │ │ │ ├── docker.io.updated.yaml │ │ │ └── docker.io.yaml │ │ ├── default_policy.json │ │ ├── etc │ │ │ └── containers │ │ │ │ ├── policy.json │ │ │ │ └── registries.d │ │ │ │ ├── docker.io-centos.yaml │ │ │ │ └── registry.access.redhat.com.yaml │ │ ├── key1.pub │ │ ├── key2.pub │ │ ├── show_policy.json │ │ └── show_policy.output │ ├── test_backends.py │ ├── test_conf.py │ ├── test_delete.py │ ├── test_diff.py │ ├── test_discovery.py │ ├── test_images.py │ ├── test_mount.py │ ├── test_pull.py │ ├── test_registries.py │ ├── test_rpm_host_install.py │ ├── test_syscontainers.py │ ├── test_trust.py │ ├── test_util.py │ └── test_util_sh.py └── unit_template.py └── vagrant.sh /.copr/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | spec := .copr/atomic.spec 4 | outdir := $(CURDIR) 5 | tmpdir := build 6 | gitdir := $(PWD)/.git 7 | 8 | rev := $(shell sed 's/\(.......\).*/\1/' $(gitdir)/$$(sed -n '/^ref:/{s/.* //;p}' $(gitdir)/HEAD)) 9 | date := $(shell date +%Y%m%d.%H%M) 10 | 11 | version := $(shell sed -n '/Version:/{s/.* //;p}' $(spec)) 12 | release := $(date).git.$(rev) 13 | 14 | srpm: $(outdir)/atomic-$(version)-$(release).src.rpm 15 | 16 | $(tmpdir)/atomic.spec: $(spec) 17 | @mkdir -p $(tmpdir) 18 | sed '/^Release:/s/\(: *\).*/\1$(release)%{?dist}/' $< >$@ 19 | 20 | $(tmpdir)/$(version).tar.gz: $(gitdir)/.. 21 | @mkdir -p $(tmpdir) 22 | tar c --exclude-vcs --exclude-vcs-ignores -C $< --transform 's|^\.|atomic-$(version)|' . | gzip -9 >$@ 23 | 24 | $(outdir)/atomic-$(version)-$(release).src.rpm: $(tmpdir)/atomic.spec $(tmpdir)/$(version).tar.gz 25 | @mkdir -p $(outdir) 26 | rpmbuild -D'_srcrpmdir $(outdir)' -D'_sourcedir $(tmpdir)' -bs $(tmpdir)/atomic.spec 27 | 28 | .PHONY: srpm 29 | -------------------------------------------------------------------------------- /.copr/atomic.spec: -------------------------------------------------------------------------------- 1 | %global debug_package %{nil} 2 | %if 0%{?fedora} <= 22 || (0%{?rhel} != 0 && 0%{?rhel} <= 7) 3 | %bcond_with python3 4 | %global pypkg python 5 | %global pysitelib %{python_sitelib} 6 | %global __python %{__python} 7 | %else 8 | %bcond_without python3 9 | %global pypkg python3 10 | %global pysitelib %{python3_sitelib} 11 | %global __python %{__python3} 12 | %endif 13 | 14 | %global commit 11707690064b6c8918b594ece30f4d8b4d8169c7 15 | %global shortcommit %(c=%{commit}; echo ${c:0:7}) 16 | 17 | Name: atomic 18 | Version: 1.22 19 | Release: %{shortcommit}-%{?dist} 20 | Summary: Tool for managing ProjectAtomic systems and containers 21 | License: LGPLv2+ 22 | URL: https://github.com/projectatomic/atomic 23 | %if 0%{?fedora} 24 | ExclusiveArch: i386 i486 i586 i686 pentium3 pentium4 athlon geode x86_64 armv3l armv4b armv4l armv4tl armv5tel armv5tejl armv6l armv6hl armv7l armv7hl armv7hnl aarch64 ppc64le s390x mips mipsel mipsr6 mipsr6el mips64 mips64el mips64r6 mips64r6el 25 | %else 26 | ExclusiveArch: x86_64 ppc64le 27 | %endif 28 | Source0: https://%{provider_prefix}/%{version}.tar.gz 29 | 30 | BuildRequires: %{pypkg}-devel 31 | BuildRequires: %{pypkg}-requests >= 2.4.3 32 | BuildRequires: %{pypkg}-setuptools 33 | BuildRequires: %{pypkg}-tools 34 | BuildRequires: policycoreutils-%{pypkg} 35 | BuildRequires: go-md2man 36 | %if 0%{?fedora} 37 | BuildRequires: go-srpm-macros 38 | %endif 39 | BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} 40 | BuildRequires: %{pypkg}-dateutil 41 | BuildRequires: %{pypkg}-dbus 42 | %if 0%{?fedora} >= 26 43 | BuildRequires: %{pypkg}-docker 44 | %else 45 | BuildRequires: %{pypkg}-docker-py 46 | %endif 47 | BuildRequires: rpm-%{pypkg} 48 | %if (0%{?rhel} != 0 && 0%{?rhel} <= 7) 49 | BuildRequires: pygobject3-base 50 | %else 51 | BuildRequires: %{pypkg}-gobject-base 52 | %endif 53 | %if 0%{?fedora} 54 | BuildRequires: ostree-devel 55 | %endif 56 | %if %{with python3} 57 | BuildRequires: %{pypkg}-PyYAML 58 | %else 59 | BuildRequires: PyYAML 60 | %endif 61 | 62 | # Not yet; https://lists.projectatomic.io/projectatomic-archives/atomic-devel/2017-April/msg00059.html 63 | #Requires: rpm-build 64 | Requires: dbus 65 | Requires: gomtree >= 0.3.1-1 66 | Requires: polkit 67 | Requires: setup 68 | Requires: skopeo >= 0.1.14-4 69 | Requires: skopeo-containers >= 0.1.14-4 70 | Requires: runc 71 | Requires: ostree 72 | Requires: rpm-%{pypkg} 73 | # https://github.com/projectatomic/atomic/pull/180 74 | Requires: %{pypkg}-dateutil 75 | Requires: %{pypkg}-dbus 76 | %if 0%{?fedora} >= 26 77 | Requires: %{pypkg}-docker >= 1.7.2 78 | %else 79 | Requires: %{pypkg}-docker-py 80 | %endif 81 | Requires: %{pypkg}-requests >= 2.4.3 82 | Requires: %{pypkg}-setuptools 83 | Requires: %{pypkg}-websocket-client >= 0.11.0 84 | Requires: %{pypkg}-six >= 1.3.0 85 | Requires: %{pypkg}-slip-dbus 86 | %if 0%{?rhel} 87 | Requires: %{pypkg}-ipaddress 88 | %endif 89 | %if (0%{?rhel} != 0 && 0%{?rhel} <= 7) 90 | Requires: pygobject3-base 91 | %else 92 | Requires: %{pypkg}-gobject-base 93 | %endif 94 | %if %{with python3} 95 | Requires: %{pypkg}-PyYAML 96 | %else 97 | Requires: PyYAML 98 | %endif 99 | 100 | %description 101 | The goal of Atomic is to provide a high level, coherent entrypoint to the 102 | system, and fill in gaps. 103 | 104 | atomic can make it easier to interact with container runtimes for different 105 | kinds of containers, such as super-privileged and system containers. 106 | 107 | The atomic host subcommand wraps rpm-ostree providing unified management. 108 | 109 | %prep 110 | %setup -q 111 | 112 | %build 113 | if [ %{pypkg} == 'python3' ]; then 114 | sed -i 's/input = raw_input/pass/' Atomic/util.py 115 | fi 116 | make PYTHON=%{__python} PYLINT=true all 117 | 118 | %install 119 | make PYTHON=%{__python} install-only DESTDIR=%{buildroot} 120 | install -dp %{buildroot}%{_sharedstatedir}/containers/%{name} 121 | 122 | # Better support for doing continuous delivery by supporting optional 123 | # components. The canonical copy of this is in `rpm-ostree.spec`. 124 | cat > autofiles.py < 0: 134 | sys.stderr.write('{0} matched {1} files\n'.format(line, len(files))) 135 | sys.stdout.write(line + '\n') 136 | else: 137 | sys.stderr.write('{0} did not match any files\n'.format(line)) 138 | EOF 139 | %{pypkg} autofiles.py > atomic.files \ 140 | '%{pysitelib}/Atomic' \ 141 | '%{pysitelib}/%{name}*.egg-info' \ 142 | '%{_sysconfdir}/%{name}.conf' \ 143 | '%{_sysconfdir}/%{name}.d' \ 144 | '%{_sysconfdir}/profile.d/%{name}.sh' \ 145 | '%{_bindir}/%{name}' \ 146 | '%{_datadir}/%{name}' \ 147 | '%{_libexecdir}/%{name}/' \ 148 | '%{_datadir}/bash-completion/completions/%{name}' \ 149 | '%{_datadir}/dbus-1/system-services/org.%{name}.service' \ 150 | '%{_datadir}/polkit-1/actions/org.%{name}.policy' \ 151 | '%{_mandir}/man1/%{name}*' 152 | 153 | #define license tag if not already defined 154 | %{!?_licensedir:%global license %doc} 155 | 156 | %files -f atomic.files 157 | %license COPYING 158 | %doc README.md 159 | %config(noreplace) %{_sysconfdir}/sysconfig/%{name} 160 | %config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.%{name}.conf 161 | %dir %{_sharedstatedir}/containers 162 | %dir %{_sharedstatedir}/containers/%{name} 163 | 164 | 165 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ## Description 3 | 4 | 5 | ## Related Bugzillas 6 | - 7 | - 8 | 9 | ## Related Issue Numbers 10 | - 11 | - 12 | 13 | ## Pull Request Checklist: 14 | 15 | If your Pull request contains new features or functions, tests are required. If the PR is a bug fix and no tests exist, please consider adding some to prevent regressions. 16 | - [ ] Unittests 17 | - [ ] Integration Tests 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *.pyc 4 | build 5 | *.log 6 | htmlcov 7 | .coverage 8 | tests/test-images/Dockerfile.secret 9 | *.orig 10 | *.rej 11 | *.out 12 | *~ 13 | gotar 14 | -------------------------------------------------------------------------------- /.papr.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora:28 2 | 3 | # NB: we also install python2 reqs here (which the builddep 4 | # does not catch since on F24 we build for py3) so that we 5 | # can do pylint for both py2 and py3 as well as reuse the 6 | # same image for RHEL & CentOS tests rather than maintaining 7 | # two separate images. 8 | 9 | RUN dnf install -y \ 10 | git \ 11 | make \ 12 | python2-pylint \ 13 | python3-pylint \ 14 | python3-slip-dbus \ 15 | python-gobject-base \ 16 | python-dbus \ 17 | pylint \ 18 | python-slip-dbus \ 19 | python2-docker \ 20 | python2-dateutil \ 21 | PyYAML \ 22 | rpm-python \ 23 | 'dnf-command(builddep)' \ 24 | && dnf builddep -y \ 25 | atomic \ 26 | && dnf clean all 27 | -------------------------------------------------------------------------------- /.papr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xeuo pipefail 3 | NO_TEST=${NO_TEST:-} 4 | 5 | if test -z "${INSIDE_CONTAINER:-}"; then 6 | 7 | if [ -f /run/ostree-booted ]; then 8 | 9 | # by default, the root LV on AH is only 3G, but we need a 10 | # bit more for our tests 11 | lvresize -r -L +5G atomicos/root || true 12 | 13 | if grep -q ID=fedora /etc/os-release; then 14 | if [ ! -e /var/tmp/ostree-unlock-ovl.* ]; then 15 | ostree admin unlock 16 | fi 17 | else 18 | # Until overlayfs and selinux get along, use remount 19 | # instead of ostree admin unlock 20 | if [ ! -w /usr ]; then 21 | mount -o remount,rw /usr 22 | fi 23 | fi 24 | else 25 | dnf install -y atomic python3-coverage docker 26 | fi 27 | 28 | # Restarting docker helps with permissions related to above. 29 | systemctl restart docker 30 | 31 | # somewhat mimic the spec conditional 32 | source /etc/os-release 33 | if [ "$ID" == fedora ]; then 34 | PYTHON=python3 35 | else 36 | PYTHON=python 37 | fi 38 | 39 | docker run --rm \ 40 | --privileged \ 41 | -v $PWD:/code \ 42 | -v /:/host \ 43 | --workdir /code \ 44 | -e INSIDE_CONTAINER=1 \ 45 | -e PYTHON=$PYTHON \ 46 | registry.fedoraproject.org/fedora:28 /code/.papr.sh 47 | 48 | # run the testsuite on the host 49 | if [ -z ${NO_TEST} ]; then 50 | PYTHON=$PYTHON ./test.sh 51 | fi 52 | exit 0 53 | fi 54 | 55 | dnf install -y \ 56 | git \ 57 | make \ 58 | python2-pylint \ 59 | python3-pylint \ 60 | python3-slip-dbus \ 61 | python-gobject-base \ 62 | python-dbus \ 63 | pylint \ 64 | python-slip-dbus \ 65 | python2-docker \ 66 | python2-dateutil \ 67 | PyYAML \ 68 | rpm-python \ 69 | 'dnf-command(builddep)' \ 70 | && dnf builddep -y \ 71 | atomic \ 72 | && dnf clean all 73 | 74 | # pylint, build, and install in the container 75 | if [ -z ${NO_TEST} ]; then 76 | make pylint-check 77 | make test-python3-pylint 78 | fi 79 | 80 | rm -rf /host/usr/bin/atomic /host/usr/lib/python*/site-packages/Atomic 81 | 82 | make PYTHON=$PYTHON PYLINT=true install DESTDIR=/host 83 | -------------------------------------------------------------------------------- /.papr.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | - master 3 | - auto 4 | - try 5 | 6 | host: 7 | distro: fedora/28/atomic 8 | specs: 9 | secondary-disk: 10 10 | 11 | context: fedora/28/atomic 12 | 13 | required: true 14 | 15 | env: 16 | ENABLE_DESTRUCTIVE: "1" 17 | 18 | tests: 19 | - ./.papr.sh 20 | 21 | artifacts: 22 | - tests.log 23 | 24 | timeout: 30m 25 | 26 | --- 27 | 28 | inherit: true 29 | 30 | host: 31 | distro: centos/7/atomic/smoketested 32 | specs: 33 | secondary-disk: 10 34 | 35 | context: centos/7/atomic 36 | 37 | --- 38 | 39 | inherit: true 40 | 41 | host: 42 | distro: fedora/28/cloud 43 | specs: 44 | secondary-disk: 10 45 | 46 | context: fedora/28/cloud 47 | 48 | # we install atomic to get the runtime reqs but of course, 49 | # its files will get overwritten by the containerized build 50 | packages: 51 | - atomic 52 | - python3-coverage 53 | - rpm-build 54 | - make 55 | - python3-PyYAML 56 | -------------------------------------------------------------------------------- /Atomic/Export.py: -------------------------------------------------------------------------------- 1 | """ 2 | export docker images, containers and volumes into a filesystem directory. 3 | """ 4 | import os 5 | 6 | from .client import AtomicDocker 7 | from . import util 8 | 9 | ATOMIC_LIBEXEC = os.environ.get('ATOMIC_LIBEXEC', '/usr/libexec/atomic') 10 | 11 | def export_docker(graph, export_location, force): 12 | """ 13 | This is a wrapper function for exporting docker images, containers 14 | and volumes. 15 | """ 16 | 17 | if not os.path.isdir(export_location): 18 | os.makedirs(export_location) 19 | 20 | with AtomicDocker() as client: 21 | dangling_images = client.images(filters={"dangling":True}, quiet=True) 22 | if any(dangling_images): 23 | if not force: 24 | choice = util.input("There are dangling images in your system. Would you like atomic to prune them [y/N]") 25 | choice = choice.strip().lower() 26 | if not choice in ['y', 'yes']: 27 | raise ValueError("Please delete dangling images before running atomic storage export") 28 | 29 | util.write_out("Deleting dangling images") 30 | util.check_call([util.default_docker(), "rmi", "-f"]+dangling_images) 31 | 32 | #Save the docker storage driver 33 | storage_driver = client.info()["Driver"] 34 | filed = open(export_location+"/info.txt", "w") 35 | filed.write(storage_driver) 36 | filed.close() 37 | 38 | #export docker images 39 | export_images(export_location) 40 | #export docker containers 41 | export_containers(graph, export_location) 42 | #export docker volumes 43 | export_volumes(graph, export_location) 44 | 45 | util.write_out("atomic export completed successfully") 46 | 47 | def export_images(export_location): 48 | """ 49 | Method for exporting docker images into a filesystem directory. 50 | """ 51 | if not os.path.isdir(export_location + "/images"): 52 | os.makedirs(export_location + "/images") 53 | 54 | images = {} 55 | with AtomicDocker() as client: 56 | for image in client.images(): 57 | Id, tags = image["Id"], image["RepoTags"] 58 | 59 | if ':' in tags: 60 | continue 61 | if Id not in images: 62 | images[Id] = [] 63 | images[Id].extend(tags) 64 | 65 | for Id in images: 66 | tags = " ".join(images[Id]) 67 | util.write_out("Exporting image: {0}".format(Id[:12])) 68 | with open(export_location + '/images/' + Id, 'w') as f: 69 | util.check_call([util.default_docker(), "save", tags], stdout=f) 70 | 71 | def export_containers(graph, export_location): 72 | """ 73 | Method for exporting docker containers into a filesystem directory. 74 | """ 75 | if not os.path.isdir(export_location + "/containers"): 76 | os.makedirs(export_location + "/containers") 77 | 78 | with AtomicDocker() as client: 79 | for container in client.containers(all=True): 80 | Id = container["Id"] 81 | 82 | util.write_out("Exporting container: {0}".format(Id[:12])) 83 | util.check_call([ATOMIC_LIBEXEC + '/migrate.sh', 84 | 'export', 85 | '--container-id=' + Id[:12], 86 | '--graph=' + graph, 87 | '--export-location=' + export_location]) 88 | 89 | def tar_create(srcdir, destfile): 90 | util.check_call(['/usr/bin/tar', '--create', '--gzip', '--selinux', 91 | '--file', destfile, '--directory', srcdir, '.']) 92 | 93 | 94 | def export_volumes(graph, export_location): 95 | """ 96 | Method for exporting docker volumes into a filesystem directory. 97 | """ 98 | if not os.path.isdir(export_location + "/volumes"): 99 | os.makedirs(export_location + "/volumes") 100 | 101 | util.write_out("Exporting volumes") 102 | tar_create(srcdir=graph + '/volumes', 103 | destfile=export_location + '/volumes/volumeData.tar.gz') 104 | 105 | if os.path.isdir(graph + "/vfs"): 106 | tar_create(srcdir=graph + '/vfs', 107 | destfile=export_location + '/volumes/vfsData.tar.gz') 108 | -------------------------------------------------------------------------------- /Atomic/Import.py: -------------------------------------------------------------------------------- 1 | """ 2 | import docker images, containers and volumes from a filesystem directory. 3 | """ 4 | import os 5 | import sys 6 | 7 | from . import util 8 | 9 | 10 | ATOMIC_LIBEXEC = os.environ.get('ATOMIC_LIBEXEC', '/usr/libexec/atomic') 11 | 12 | def import_docker(graph, import_location, assumeyes): 13 | """ 14 | This is a wrapper function for importing docker images, containers 15 | and volumes. 16 | """ 17 | 18 | if not os.path.isdir(import_location): 19 | sys.exit("{0} does not exist".format(import_location)) 20 | #import docker images 21 | import_images(import_location) 22 | #import docker containers 23 | import_containers(graph, import_location) 24 | #import docker volumes 25 | import_volumes(graph, import_location) 26 | 27 | util.write_out("atomic import completed successfully") 28 | confirm = "" 29 | if assumeyes: 30 | confirm = "yes" 31 | else: 32 | confirm = util.input("Would you like to cleanup (rm -rf {0}) the temporary directory [y/N]" 33 | .format(import_location)) 34 | confirm = confirm.strip().lower() 35 | if confirm in ['y', 'yes']: 36 | util.write_out("Deleting {0}".format(import_location)) 37 | util.check_call(['/usr/bin/rm', '-rf', import_location]) 38 | util.write_out("Please restart docker daemon for the changes to take effect") 39 | 40 | def import_images(import_location): 41 | """ 42 | Method for importing docker images from a filesystem directory. 43 | """ 44 | subdir = import_location + '/images' 45 | images = os.listdir(subdir) 46 | for image in images: 47 | util.write_out("Importing image: {0}".format(image[:12])) 48 | with open(subdir + '/' + image) as f: 49 | util.check_call([util.default_docker(), "load"], stdin=f) 50 | 51 | def import_containers(graph, import_location): 52 | """ 53 | Method for importing docker containers from a filesystem directory. 54 | """ 55 | subdir = import_location + '/containers' 56 | containers = os.listdir(subdir) 57 | for cnt in containers: 58 | cnt = cnt[8:] # strip off the "migrate-" prefix 59 | util.write_out("Importing container: {0}".format(cnt[:12])) 60 | util.check_call([ATOMIC_LIBEXEC + '/migrate.sh', 61 | 'import', 62 | '--container-id=' + cnt, 63 | '--graph=' + graph, 64 | '--import-location=' + import_location]) 65 | 66 | def tar_extract(srcfile, destdir): 67 | util.check_call(['/usr/bin/tar', '--extract', '--gzip', '--selinux', 68 | '--file', srcfile, '--directory', destdir]) 69 | 70 | def import_volumes(graph, import_location): 71 | """ 72 | Method for importing docker volumes from a filesystem directory. 73 | """ 74 | 75 | volfile = import_location + '/volumes/volumeData.tar.gz' 76 | if os.path.isfile(volfile): 77 | util.write_out("Importing volumes") 78 | tar_extract(srcfile=volfile, 79 | destdir=graph + '/volumes') 80 | 81 | vfsfile = import_location + '/volumes/vfsData.tar.gz' 82 | if os.path.isfile(vfsfile) and os.path.isdir(graph + "/vfs"): 83 | tar_extract(srcfile=vfsfile, 84 | destdir=graph + '/vfs') 85 | -------------------------------------------------------------------------------- /Atomic/__init__.py: -------------------------------------------------------------------------------- 1 | from .pulp import PulpServer, PulpConfig 2 | from .satellite import SatelliteServer, SatelliteConfig 3 | from .atomic import Atomic 4 | from .util import write_out 5 | 6 | #https://bitbucket.org/logilab/pylint/issues/36/ 7 | #pylint: disable=no-member 8 | 9 | # When changinig the version here, also change in the 10 | # .copr/atomic.spec line 18. 11 | __version__ = '1.22.1' 12 | __author__ = 'Daniel Walsh' 13 | __author_email__ = 'dwalsh@redhat.com' 14 | 15 | -------------------------------------------------------------------------------- /Atomic/backends/__init__.py: -------------------------------------------------------------------------------- 1 | # Backends 2 | 3 | -------------------------------------------------------------------------------- /Atomic/backends/_docker_errors.py: -------------------------------------------------------------------------------- 1 | class DockerObjectNotFound(ValueError): 2 | def __init__(self, msg): 3 | super(DockerObjectNotFound, self).__init__("Unable to associate '{}' with an image or container".format(msg)) 4 | 5 | class NoDockerDaemon(Exception): 6 | def __init__(self): 7 | super(NoDockerDaemon, self).__init__("The docker daemon does not appear to be running.") 8 | -------------------------------------------------------------------------------- /Atomic/backends/backend.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod, ABCMeta, abstractproperty 2 | 3 | 4 | class Backend(object): #pylint: disable=metaclass-assignment 5 | # Mark the class as abstract 6 | __metaclass__ = ABCMeta 7 | 8 | @abstractproperty 9 | def backend(self): 10 | return 'Should never use this' 11 | 12 | @abstractmethod 13 | def inspect_image(self, image): 14 | """ 15 | Returns the results of an inspected image as an image object 16 | :param image: 17 | :return: img_obj 18 | """ 19 | pass 20 | 21 | @abstractmethod 22 | def inspect_container(self, container): 23 | """ 24 | Inspect a container 25 | :param container: 26 | :return: con_obj 27 | """ 28 | pass 29 | 30 | @abstractmethod 31 | def pull_image(self, image, remote_image_obj, **kwargs): 32 | """ 33 | Pulls an image to the backend 34 | :param image: 35 | :param pull_args: 36 | :return: 37 | """ 38 | pass 39 | 40 | @abstractmethod 41 | def install(self, image, name, **kwargs): 42 | """ 43 | Installs an image on a backend 44 | :param image: 45 | :param name: 46 | :return: 47 | """ 48 | pass 49 | 50 | @abstractmethod 51 | def uninstall(self, iobject, name=None, **kwargs): 52 | """ 53 | Uninstalls an image from a backend 54 | :param name: 55 | :return: 56 | """ 57 | pass 58 | 59 | @abstractmethod 60 | def version(self, image): 61 | """ 62 | Return a list of layer objects 63 | :param image: 64 | :return: list of layer objects 65 | """ 66 | pass 67 | 68 | @abstractmethod 69 | def update(self, name, **kwargs): 70 | """ 71 | Downloads latest image from registry 72 | :param image: 73 | :return: 74 | """ 75 | pass 76 | 77 | @abstractmethod 78 | def get_containers(self): 79 | """ 80 | Get containers for the backend 81 | :return: list of container objects 82 | """ 83 | pass 84 | 85 | @abstractmethod 86 | def get_images(self, get_all=False): 87 | """ 88 | Get images for the backend 89 | :param get_all: bool 90 | :return: list of image objects 91 | """ 92 | pass 93 | 94 | @abstractmethod 95 | def delete_image(self, image, force=False): 96 | """ 97 | Delete image 98 | :param image: 99 | :param force: 100 | :return: 101 | """ 102 | pass 103 | 104 | @abstractmethod 105 | def delete_container(self, container, force=False): 106 | pass 107 | 108 | @abstractmethod 109 | def start_container(self, name): 110 | pass 111 | 112 | @abstractmethod 113 | def stop_container(self, con_obj, **kwargs): 114 | pass 115 | 116 | @abstractmethod 117 | def prune(self): 118 | pass 119 | 120 | @abstractmethod 121 | def has_image(self, img): 122 | """ 123 | Returns an img object if backend has the image or None 124 | :param img: 125 | :return: img_obj or None 126 | """ 127 | pass 128 | 129 | @abstractmethod 130 | def has_container(self, container): 131 | """ 132 | Returns a container obj if valid or None 133 | :param container: 134 | :return: 135 | """ 136 | pass 137 | 138 | @abstractmethod 139 | def validate_layer(self, layer): 140 | pass 141 | 142 | @abstractmethod 143 | def run(self, iobject, **kwargs): 144 | pass 145 | 146 | 147 | @abstractproperty 148 | def available(self): 149 | pass 150 | 151 | @abstractmethod 152 | def tag_image(self, src, dest): 153 | pass 154 | -------------------------------------------------------------------------------- /Atomic/client.py: -------------------------------------------------------------------------------- 1 | import docker 2 | import sys 3 | import requests 4 | from Atomic.backends._docker_errors import NoDockerDaemon 5 | 6 | def get_docker_client(): 7 | """ 8 | Universal method to use docker.client() 9 | """ 10 | kwargs = {"version": "auto"} 11 | kwargs.update(docker.utils.kwargs_from_env(assert_hostname=False)) 12 | try: 13 | client = docker.APIClient #pylint: disable=no-member 14 | except AttributeError: 15 | client = docker.Client #pylint: disable=no-member 16 | try: 17 | return client(**kwargs) 18 | except docker.errors.DockerException: 19 | raise NoDockerDaemon() 20 | 21 | def check_if_python2(): 22 | if int(sys.version_info[0]) < 3: 23 | _input = raw_input # pylint: disable=undefined-variable,raw_input-builtin 24 | return _input, True 25 | else: 26 | _input = input # pylint: disable=input-builtin 27 | return _input, False 28 | 29 | class AtomicDocker(): 30 | def __init__(self): 31 | self._client = None 32 | 33 | @property 34 | def _dockerclient(self): 35 | if not self._client: 36 | self._client = get_docker_client() 37 | return self._client 38 | 39 | def __dir__(self): 40 | return dir(self._dockerclient) 41 | 42 | def __repr__(self): 43 | return self._dockerclient.__repr__() 44 | 45 | def __enter__(self): 46 | return self 47 | 48 | def __exit__(self, typ, value, traceback): 49 | self.close() 50 | 51 | def __getattr__(self, name): 52 | return self.__getattribute__(name) 53 | 54 | def __getattribute__(self, name): 55 | # Avoid recursion for self._dockerclient 56 | if name == "_dockerclient" or name == "_client": 57 | return object.__getattribute__(self, name) 58 | obj = self._dockerclient 59 | try: 60 | attr = docker.Client.__getattribute__(obj, name) #pylint: disable=no-member 61 | except AttributeError: 62 | attr = docker.APIClient.__getattribute__(obj, name) #pylint: disable=no-member 63 | if hasattr(attr, '__call__'): 64 | def newfunc(*args, **kwargs): 65 | try: 66 | result = attr(*args, **kwargs) 67 | return iter_subs(result) 68 | except requests.exceptions.ConnectionError as e: 69 | if name == "containers" or name == "images": 70 | return [] 71 | raise e 72 | return newfunc 73 | else: 74 | return attr 75 | 76 | def close(self): 77 | if self._client != None: 78 | self._client.close() 79 | 80 | is_python2 = check_if_python2()[1] 81 | 82 | # Known keys that contain sha26: preceding value 83 | SUB_KEYS = ['Parent', 'Id', 'Image', 'ImageID'] 84 | ALGO = "sha256:" 85 | 86 | 87 | def no_shaw(value): 88 | return value.replace(ALGO, "") 89 | 90 | 91 | def iter_subs(tree, key=None): 92 | """ 93 | Takes a structure like a dict, list of dicts ... and 94 | recursively walks the structure to replace any value it 95 | finds that starts with the algo with a DockerID object. 96 | """ 97 | if isinstance(tree, set): 98 | tree = set([iter_subs(x) for x in tree]) 99 | elif isinstance(tree, frozenset): 100 | tree = frozenset([iter_subs(x) for x in tree]) 101 | elif isinstance(tree, str): 102 | if str(tree).startswith(ALGO) and key in SUB_KEYS: 103 | return no_shaw(tree) 104 | # In py2, it is unicode and not str 105 | elif is_python2 and isinstance(tree, unicode) and key in SUB_KEYS: #pylint: disable=undefined-variable,unicode-builtin 106 | if str(tree).startswith(ALGO): 107 | return no_shaw(tree) 108 | elif isinstance(tree, dict): 109 | for k, v in tree.items(): 110 | tree[k] = iter_subs(v, key=k) 111 | elif isinstance(tree, list): 112 | if is_python2: 113 | if all(isinstance(x, unicode) for x in tree) and all(j.startswith(ALGO) for j in tree): # pylint: disable=undefined-variable,unicode-builtin 114 | return [no_shaw(i) for i in tree] 115 | else: 116 | if all(isinstance(x, str) for x in tree) and all(j.startswith(ALGO) for j in tree): 117 | return [no_shaw(i) for i in tree] 118 | for i in range(len(tree)): 119 | tree[i] = iter_subs(tree[i]) 120 | return tree 121 | -------------------------------------------------------------------------------- /Atomic/delete.py: -------------------------------------------------------------------------------- 1 | from . import Atomic 2 | from . import util 3 | import sys 4 | from Atomic.backendutils import BackendUtils 5 | 6 | ATOMIC_CONFIG = util.get_atomic_config() 7 | storage = ATOMIC_CONFIG.get('default_storage', "docker") 8 | 9 | class Delete(Atomic): 10 | def __init__(self): 11 | super(Delete, self).__init__() 12 | self.be = None 13 | 14 | def delete_image(self): 15 | """ 16 | Mark given image(s) for deletion from registry 17 | :return: 0 if all images marked for deletion, otherwise 2 on any failure 18 | """ 19 | if self.args.debug: 20 | util.write_out(str(self.args)) 21 | 22 | if (len(self.args.delete_targets) > 0 and self.args.all) or (len(self.args.delete_targets) < 1 and not self.args.all): 23 | raise ValueError("You must select --all or provide a list of images to delete.") 24 | 25 | beu = BackendUtils() 26 | delete_objects = [] 27 | 28 | # We need to decide on new returns for dbus because we now check image 29 | # validity prior to executing the delete. If there is going to be a 30 | # failure, it will be here. 31 | # 32 | # The failure here is basically that it couldnt verify/find the image. 33 | 34 | if self.args.all: 35 | if self.args.storage: 36 | self.be = beu.get_backend_from_string(self.args.storage) 37 | delete_objects = self.be.get_images(get_all=True) 38 | else: 39 | delete_objects = beu.get_images(get_all=True) 40 | else: 41 | for image in self.args.delete_targets: 42 | _, img_obj = beu.get_backend_and_image_obj(image, str_preferred_backend=self.args.storage or storage, required=True if self.args.storage else False) 43 | delete_objects.append(img_obj) 44 | 45 | if self.args.remote: 46 | return self._delete_remote(self.args.delete_targets) 47 | if len(delete_objects) == 0: 48 | raise ValueError("No images to delete.") 49 | _image_names = [] 50 | for del_obj in delete_objects: 51 | if del_obj.repotags: 52 | _image_names.append(len(del_obj.repotags[0])) 53 | else: 54 | _image_names.append(len(del_obj.id)) 55 | max_img_name = max(_image_names) + 2 56 | 57 | if not self.args.assumeyes: 58 | util.write_out("Do you wish to delete the following images?\n") 59 | else: 60 | util.write_out("The following images will be deleted.\n") 61 | 62 | two_col = " {0:" + str(max_img_name) + "} {1}" 63 | util.write_out(two_col.format("IMAGE", "STORAGE")) 64 | for del_obj in delete_objects: 65 | image = None if not del_obj.repotags else del_obj.repotags[0] 66 | if image is None or "" in image: 67 | image = del_obj.id[0:12] 68 | util.write_out(two_col.format(image, del_obj.backend.backend)) 69 | if not self.args.assumeyes: 70 | confirm = util.input("\nConfirm (y/N) ") 71 | confirm = confirm.strip().lower() 72 | if not confirm in ['y', 'yes']: 73 | util.write_err("User aborted delete operation for {}".format(self.args.delete_targets or "all images")) 74 | sys.exit(2) 75 | 76 | # Perform the delete 77 | for del_obj in delete_objects: 78 | del_obj.backend.delete_image(del_obj.input_name, force=self.args.force) 79 | 80 | # We need to return something here for dbus 81 | return 0 82 | 83 | def prune_images(self): 84 | """ 85 | Remove dangling images from registry 86 | :return: 0 if all images deleted or no dangling images found 87 | """ 88 | 89 | if self.args.debug: 90 | util.write_out(str(self.args)) 91 | 92 | beu = BackendUtils() 93 | for backend in beu.available_backends: 94 | be = backend() 95 | be.prune() 96 | 97 | return 0 98 | 99 | def _delete_remote(self, targets): 100 | results = 0 101 | for target in targets: 102 | # _convert_to_skopeo requires registry v1 support while delete requires v2 support 103 | # args, img = self.syscontainers._convert_to_skopeo(target) 104 | 105 | args = [] 106 | if "http:" in target: 107 | args.append("--insecure") 108 | 109 | for i in ["oci:", "http:", "https:"]: 110 | img = target.replace(i, "docker:") 111 | 112 | if not img.startswith("docker:"): 113 | img = "docker://" + img 114 | 115 | try: 116 | util.skopeo_delete(img, args) 117 | util.write_out("Image {} marked for deletion".format(img)) 118 | except ValueError as e: 119 | util.write_err("Failed to mark Image {} for deletion: {}".format(img, e)) 120 | results = 2 121 | return results 122 | 123 | 124 | -------------------------------------------------------------------------------- /Atomic/discovery.py: -------------------------------------------------------------------------------- 1 | from . import util 2 | 3 | 4 | class RegistryInspectError(Exception): 5 | pass 6 | 7 | 8 | class RegistryAuthError(Exception): 9 | pass 10 | 11 | 12 | class RegistryInspect(): 13 | 14 | def __init__(self, registry=None, repo=None, image=None, tag=None, digest=None, orig_input=None, debug=False): 15 | self.debug = debug 16 | self.registries = util.get_registries() 17 | self.registry = registry 18 | self.repo = repo 19 | self.image = image 20 | self.tag = tag 21 | self.digest = digest 22 | self.orig_input = orig_input 23 | self._remote_inspect = None 24 | self._fqdn = None 25 | 26 | if self.debug: 27 | util.output_json(self.registries) 28 | 29 | @property 30 | def fqdn(self): 31 | if not self._fqdn: 32 | self._fqdn = self.assemble_fqdn(include_tag=True) if self.registry else self.find_image_on_registry() 33 | return self._fqdn 34 | 35 | @fqdn.setter 36 | def fqdn(self, value): 37 | self._fqdn = value 38 | 39 | def inspect(self): 40 | if self.registry: 41 | inspect_data = util.skopeo_inspect("docker://{}".format(self.fqdn), return_json=True) 42 | else: 43 | inspect_data = self._remote_inspect 44 | inspect_data['Tag'] = self.tag 45 | inspect_data['Name'] = self.assemble_fqdn(include_tag=False) 46 | return inspect_data 47 | 48 | def get_manifest(self, return_json=True): 49 | assert(self.fqdn is not None) 50 | return util.skopeo_inspect("docker://{}".format(self.fqdn), return_json=return_json, args=['--raw']) 51 | 52 | @property 53 | def remote_id(self): 54 | result = self.get_manifest() 55 | if result.get('config'): 56 | return result['config'].get('digest', None) 57 | return None 58 | 59 | def assemble_fqdn(self, include_tag=True, registry=None): 60 | fqdn = "{}".format(registry or self.registry) 61 | if self.repo: 62 | fqdn = "{}/{}".format(fqdn, self.repo) 63 | elif fqdn == "docker.io": # and no repo specified 64 | fqdn = fqdn + "/library" 65 | fqdn += "/{}".format(self.image) 66 | if include_tag: 67 | if self.tag: 68 | fqdn += ":{}".format(self.tag) 69 | elif self.digest: 70 | fqdn += "@{}".format(self.digest) 71 | return fqdn 72 | 73 | def find_image_on_registry(self, quiet=False): 74 | """ 75 | Find the fully qualified image name for given input when 76 | registry is unknown 77 | :return: String fqdn 78 | """ 79 | if self.debug: 80 | for i in [x for x in self.registries if x['search']]: 81 | util.write_out(repr(i)) 82 | 83 | registries = [i['name'] for i in [x for x in self.registries if x['search']]] 84 | for registry in registries: 85 | fqdn = self.assemble_fqdn(registry=registry, include_tag=True) 86 | if not quiet: 87 | util.write_out("Trying {}...".format(fqdn)) 88 | try: 89 | result = util.skopeo_inspect("docker://{}".format(fqdn), return_json=True) 90 | self._remote_inspect = result 91 | return fqdn 92 | except ValueError as e: 93 | if not quiet: 94 | util.write_err("Failed: {}".format(e)) 95 | continue 96 | raise RegistryInspectError("Unable to find {}".format(self.orig_input)) 97 | 98 | -------------------------------------------------------------------------------- /Atomic/objects/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Atomic/objects/container.py: -------------------------------------------------------------------------------- 1 | from Atomic.util import output_json 2 | import datetime 3 | 4 | class Container(object): 5 | def __init__(self, input_name, backend=None): 6 | 7 | # Required 8 | self._name = None 9 | self.id = None 10 | self._created = None 11 | self.status = None 12 | self.input_name = input_name 13 | self.original_structure = None 14 | self.deep = False 15 | self._backend = backend 16 | self.runtime = backend.backend 17 | self.image = None 18 | self.image_name = None 19 | self._command = None 20 | self.state = None 21 | self.vulnerable = False 22 | self.labels = None 23 | self._user_command = None 24 | self.mount_path = None 25 | 26 | # Optional 27 | self.running = False 28 | # Instantiate 29 | self._instantiate() 30 | self.stop_args = None 31 | 32 | def _instantiate(self): 33 | self._setup_common() 34 | return self 35 | 36 | def _setup_common(self): 37 | # Items common to backends can go here. 38 | pass 39 | 40 | def get_label(self, label): 41 | if self.labels: 42 | return self.labels.get(label.lower(), None) or self.labels.get(label.upper(), None) 43 | return None 44 | 45 | def dump(self): 46 | # Helper function to dump out known variables in pretty-print style 47 | class_vars = dict(vars(self)) 48 | foo = {x: class_vars[x] for x in class_vars if not callable(getattr(self, x)) and not x.startswith('__') 49 | and not x.endswith('_backend')} 50 | output_json(foo) 51 | 52 | @property 53 | def backend(self): 54 | return self._backend 55 | 56 | @backend.setter 57 | def backend(self, value): 58 | self._backend = value 59 | 60 | @property 61 | def type(self): 62 | return 'container' 63 | 64 | @property 65 | def created(self): 66 | return str(datetime.datetime.fromtimestamp(self._created)) 67 | 68 | @property 69 | def created_raw(self): 70 | return self._created 71 | 72 | @created.setter 73 | def created(self, value): 74 | self._created = value 75 | 76 | @property 77 | def command(self): 78 | cmd = self._command if self._command is not None else ['/bin/sh'] 79 | return cmd 80 | 81 | @command.setter 82 | def command(self, value): 83 | self._command = value 84 | 85 | @property 86 | def interactive(self): 87 | config = self.original_structure['Config'] 88 | if all([config.get('AttachStdin', False), config.get('AttachStdout', False), config.get('AttachStderr', False)]): 89 | return True 90 | return False 91 | 92 | @property 93 | def name(self): 94 | return str(self._name) 95 | 96 | @name.setter 97 | def name(self, value): 98 | self._name = value[1:] if value[0] == '/' else value 99 | 100 | @property 101 | def user_command(self): 102 | return self._user_command 103 | 104 | @user_command.setter 105 | def user_command(self, value): 106 | self._user_command = value 107 | 108 | -------------------------------------------------------------------------------- /Atomic/objects/layer.py: -------------------------------------------------------------------------------- 1 | from Atomic.util import output_json 2 | from Atomic.client import no_shaw 3 | 4 | class Layer(object): # pylint: disable=eq-without-hash 5 | def __init__(self, img_input): 6 | self.id = None 7 | self.name = None 8 | self.version = None 9 | self.release = None 10 | self.repotags = None 11 | self.parent = None 12 | self.remote = False 13 | self.digest = None 14 | self.backend = None 15 | 16 | if type(img_input) is dict: 17 | pass 18 | else: 19 | self._instantiate_from_image_object(img_input) 20 | 21 | def _instantiate_from_image_object(self, img_obj): 22 | self.id = img_obj.id 23 | self.name = img_obj.get_label('Name') or img_obj.name or img_obj.image 24 | self.remote = img_obj.remote 25 | self.version = img_obj.version 26 | self.release = img_obj.release 27 | self.repotags = img_obj.repotags 28 | # This needs to be resolved for future docker versions 29 | self.parent = img_obj.parent 30 | self.digest = img_obj.digest 31 | self.backend = img_obj.backend 32 | return self 33 | 34 | def _instantiate_from_dict(self): 35 | return self 36 | 37 | def __eq__(self, other): 38 | if self.long_version == other.long_version: 39 | return True 40 | return False 41 | 42 | def __ne__(self, other): 43 | if self.long_version != other.long_version: 44 | return True 45 | return False 46 | 47 | def dump(self): 48 | # helper function to dump out known variables/values in pretty-print style 49 | class_vars = dict(vars(self)) 50 | foo = {x: class_vars[x] for x in class_vars if not callable(getattr(self, x)) and not x.startswith('__') 51 | and not x.endswith('_backend')} 52 | output_json(foo) 53 | 54 | @property 55 | def long_version(self): 56 | _version = "" 57 | if self.version: 58 | _version += "{}".format(self.version) 59 | if self.release: 60 | if self.version: 61 | _version += "-" 62 | _version += "{}".format(self.release) 63 | if not _version: 64 | return no_shaw(self.id or self.digest) 65 | return _version 66 | -------------------------------------------------------------------------------- /Atomic/pull.py: -------------------------------------------------------------------------------- 1 | try: 2 | from . import Atomic 3 | except ImportError: 4 | from atomic import Atomic # pylint: disable=relative-import 5 | from .util import get_atomic_config, write_out, check_storage_is_available 6 | from Atomic.backendutils import BackendUtils 7 | 8 | ATOMIC_CONFIG = get_atomic_config() 9 | 10 | 11 | _storage = ATOMIC_CONFIG.get('default_storage', "docker") 12 | 13 | def cli(subparser): 14 | # atomic pull 15 | pullp = subparser.add_parser("pull", help=_("pull latest image from a repository"), 16 | epilog="pull the latest specified image from a repository.") 17 | pullp.set_defaults(_class=Pull, func='pull_image') 18 | pullp.add_argument("--storage", dest="storage", default=None, 19 | help=_("Specify the storage. Default is currently '%s'. You can" 20 | " change the default by editing /etc/atomic.conf and changing" 21 | " the 'default_storage' field." % _storage)) 22 | pullp.add_argument("--src-creds", dest="src_creds", default=None, 23 | help=_("Use USERNAME[:PASSWORD] for accessing the source registry.")) 24 | pullp.add_argument("-t", "--type", dest="reg_type", default=None, 25 | help=_("Pull from an alternative registry type.")) 26 | pullp.add_argument("image", help=_("image id")) 27 | 28 | 29 | class Pull(Atomic): 30 | def __init__(self, policy_filename=None): 31 | """ 32 | :param policy_filename: override policy filename 33 | """ 34 | super(Pull, self).__init__() 35 | self.policy_filename=policy_filename 36 | self.be_utils = BackendUtils() 37 | 38 | def pull_image(self): 39 | storage_set = False if self.args.storage is None else True 40 | storage = _storage if not storage_set else self.args.storage 41 | check_storage_is_available(storage) 42 | if self.args.debug: 43 | write_out(str(self.args)) 44 | 45 | src_creds = getattr(self.args, 'src_creds', None) 46 | if src_creds == "": 47 | src_creds = None 48 | 49 | be_utils = BackendUtils() 50 | be = be_utils.get_backend_from_string(storage) 51 | self.args.policy_filename = self.policy_filename 52 | try: 53 | if be.backend == 'docker': 54 | remote_image_obj = be.make_remote_image(self.args.image) 55 | if remote_image_obj.is_system_type and not storage_set: 56 | be = be_utils.get_backend_from_string('ostree') 57 | be_utils.message_backend_change('docker', 'ostree') 58 | elif be.backend == "containers-storage": 59 | remote_image_obj = be.make_remote_image(self.args.image) 60 | else: 61 | remote_image_obj = None 62 | be.pull_image(self.args.image, remote_image_obj, debug=self.args.debug, assumeyes=self.args.assumeyes, src_creds=src_creds) 63 | except ValueError as e: 64 | raise ValueError("Failed: {}".format(e)) 65 | return 0 66 | 67 | 68 | -------------------------------------------------------------------------------- /Atomic/stop.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from . import util 4 | from Atomic.backendutils import BackendUtils 5 | import sys 6 | 7 | try: 8 | from . import Atomic 9 | except ImportError: 10 | from atomic import Atomic # pylint: disable=relative-import 11 | 12 | def cli(subparser): 13 | # atomic stop 14 | stopp = subparser.add_parser( 15 | "stop", help=_("execute container image stop method"), 16 | epilog="atomic will just stop the container if it is running, if " 17 | "image does not specify LABEL STOP") 18 | stopp.set_defaults(_class=Stop, func='stop') 19 | util.add_opt(stopp) 20 | stopp.add_argument("container", help=_("container name or ID")) 21 | stopp.add_argument("--display", default=False, action="store_true", 22 | help=_("preview the command that %s would execute") % sys.argv[0]) 23 | stopp.add_argument("args", nargs=argparse.REMAINDER, 24 | help=_("Additional arguments appended to the image " 25 | "stop method")) 26 | 27 | ATOMIC_CONFIG = util.get_atomic_config() 28 | storage = ATOMIC_CONFIG.get('default_storage', "docker") 29 | 30 | class Stop(Atomic): 31 | def __init__(self): # pylint: disable=useless-super-delegation 32 | super(Stop, self).__init__() 33 | 34 | def stop(self): 35 | 36 | if self.args.debug: 37 | util.write_out(str(self.args)) 38 | 39 | beu = BackendUtils() 40 | be, con_obj = beu.get_backend_and_container_obj(self.args.container, storage) 41 | be.stop_container(con_obj, atomic=self, args=self.args) 42 | return 0 43 | 44 | -------------------------------------------------------------------------------- /Atomic/tag.py: -------------------------------------------------------------------------------- 1 | from . import Atomic 2 | from . import util 3 | from Atomic.backendutils import BackendUtils 4 | 5 | ATOMIC_CONFIG = util.get_atomic_config() 6 | storage = ATOMIC_CONFIG.get('default_storage', "docker") 7 | 8 | class Tag(Atomic): 9 | def __init__(self): 10 | super(Tag, self).__init__() 11 | self.be = None 12 | 13 | def tag_image(self): 14 | """ 15 | Tag an image with a different name 16 | :return: 0 if the tag was created 17 | """ 18 | if self.args.debug: 19 | util.write_out(str(self.args)) 20 | 21 | beu = BackendUtils() 22 | 23 | backend = None 24 | if self.args.storage: 25 | backend = beu.get_backend_from_string(self.args.storage) 26 | image = backend.has_image(self.args.src) 27 | 28 | else: 29 | backend, image = beu.get_backend_and_image_obj(self.args.src, required=False) 30 | 31 | if not backend or not image: 32 | raise ValueError("Cannot find image {}.".format(self.args.src)) 33 | 34 | backend.tag_image(self.args.src, self.args.target) 35 | 36 | # We need to return something here for dbus 37 | return 0 38 | -------------------------------------------------------------------------------- /Atomic/uninstall.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from . import util 3 | from .util import add_opt 4 | from .install import INSTALL_ARGS 5 | from Atomic.backendutils import BackendUtils 6 | import sys 7 | from Atomic.backends._ostree import OSTreeBackend 8 | 9 | try: 10 | from . import Atomic 11 | except ImportError: 12 | from atomic import Atomic # pylint: disable=relative-import 13 | 14 | ATOMIC_CONFIG = util.get_atomic_config() 15 | _storage = ATOMIC_CONFIG.get('default_storage', "docker") 16 | 17 | def cli(subparser): 18 | # atomic uninstall 19 | uninstallp = subparser.add_parser( 20 | "uninstall", help=_("execute container image uninstall method"), 21 | epilog="atomic uninstall attempts to read the LABEL UNINSTALL " 22 | "field in the image, if it does not exist atomic will " 23 | "remove the image from your machine. You could add a " 24 | "LABEL UNINSTALL command to your Dockerfile like: 'LABEL " 25 | "UNINSTALL %s'" % Uninstall.print_uninstall()) 26 | uninstallp.set_defaults(_class=Uninstall, func='uninstall') 27 | add_opt(uninstallp) 28 | uninstallp.add_argument("-n", "--name", dest="name", default=None, 29 | help=_("name of container")) 30 | uninstallp.add_argument("-f", "--force", default=False, dest="force", 31 | action="store_true", 32 | help=_("remove all containers based on this " 33 | "image")) 34 | uninstallp.add_argument("--display", default=False, action="store_true", 35 | help=_("preview the command that %s would execute") % sys.argv[0]) 36 | uninstallp.add_argument("image", help=_("container image")) 37 | uninstallp.add_argument("--storage", dest="storage", default=None, 38 | help=_("Specify the storage. Default is currently '%s'. You can change the default " 39 | "by editing /etc/atomic.conf and changing the 'default_storage' field." % _storage)) 40 | uninstallp.add_argument("args", nargs=argparse.REMAINDER, 41 | help=_("Additional arguments appended to the " 42 | "image uninstall method")) 43 | 44 | class Uninstall(Atomic): 45 | def __init__(self): # pylint: disable=useless-super-delegation 46 | super(Uninstall, self).__init__() 47 | 48 | def uninstall(self): 49 | if self.args.debug: 50 | util.write_out(str(self.args)) 51 | 52 | beu = BackendUtils() 53 | try: 54 | be, img_obj = beu.get_backend_and_image_obj(self.args.image, str_preferred_backend=self.args.storage) 55 | except ValueError as e: 56 | if 'ostree' in [x().backend for x in beu.available_backends]: 57 | ost = OSTreeBackend() 58 | img_obj = ost.has_container(self.args.image) 59 | if not img_obj: 60 | raise ValueError(e) 61 | be = ost 62 | be.uninstall(img_obj, name=self.args.name, atomic=self, ignore=self.args.ignore) 63 | return 0 64 | 65 | 66 | @staticmethod 67 | def print_uninstall(): 68 | return "%s %s %s" % (util.default_docker(), " ".join(INSTALL_ARGS), "/usr/bin/UNINSTALLCMD") 69 | 70 | -------------------------------------------------------------------------------- /Atomic/update.py: -------------------------------------------------------------------------------- 1 | try: 2 | from . import Atomic 3 | except ImportError: 4 | from atomic import Atomic # pylint: disable=relative-import 5 | 6 | import argparse 7 | from Atomic.backendutils import BackendUtils 8 | from Atomic.util import get_atomic_config, write_out, write_err, Decompose 9 | import sys 10 | 11 | ATOMIC_CONFIG = get_atomic_config() 12 | storage = ATOMIC_CONFIG.get('default_storage', "docker") 13 | 14 | def cli(subparser, hidden=False): 15 | # atomic update 16 | if hidden: 17 | updatep = subparser.add_parser("update", argument_default=argparse.SUPPRESS) 18 | else: 19 | updatep = subparser.add_parser( 20 | "update", help=_("pull latest container image from repository"), 21 | epilog="downloads the latest container image. If a previously created " 22 | "container based on this image exists, the container will " 23 | "continue to use the old image. Use --force to remove the " 24 | "outdated container.") 25 | updatep.set_defaults(_class=Update, func='update') 26 | updatep.add_argument("-f", "--force", default=False, dest="force", 27 | action="store_true", 28 | help=_("remove all containers based on this image")) 29 | updatep.add_argument("-a", "--all", default=False, dest="all", 30 | action="store_true", 31 | help=_("update all the images")) 32 | updatep.add_argument("--storage", default=None, dest="storage", 33 | help=_("Specify the storage of the image. Defaults to: %s" % storage)) 34 | updatep.add_argument("image", nargs='?', help=_("container image")) 35 | 36 | class Update(Atomic): 37 | def __init__(self): # pylint: disable=useless-super-delegation 38 | super(Update, self).__init__() 39 | 40 | def update_all_images(self, be, debug): 41 | images = be.get_images() 42 | images_by_name = {} 43 | for i in images: 44 | if i.repotags is None: 45 | continue 46 | 47 | img_name = i.repotags[0] 48 | d = Decompose(img_name) 49 | if d.registry == "": 50 | write_err("Image {} not fully qualified: skipping".format(img_name)) 51 | continue 52 | 53 | images_by_name[img_name] = i 54 | could_not_pull = {} 55 | pulled = {} 56 | 57 | write_out("Checking image {}...".format(img_name)) 58 | try: 59 | be.update(img_name, debug=debug, force=False, image_object=i) 60 | pulled[img_name] = True 61 | except: # pylint: disable=bare-except 62 | could_not_pull[img_name] = True 63 | 64 | def get_status(img_name, pre_id, post_id): 65 | COLOR_RED = 31 66 | COLOR_GREEN = 32 67 | 68 | if img_name in could_not_pull.keys(): 69 | return "Could not pull", COLOR_RED 70 | 71 | if pre_id != post_id: 72 | return "Updated now", COLOR_GREEN 73 | 74 | return "Updated", COLOR_GREEN 75 | 76 | def colored(line, color): 77 | if sys.stdout.isatty(): 78 | return "\x1b[1;%dm%s\x1b[0m" % (color, line) 79 | else: 80 | return line 81 | 82 | cols = "{0:50} {1:32} {2:32} {3:15}" 83 | 84 | write_out("\nSUMMARY\n") 85 | write_out(cols.format("Image", "Image ID before update", "Image ID after update", "Status")) 86 | for k, v in images_by_name.items(): 87 | new_image = be.inspect_image(k) 88 | status, color = get_status(k, v.id, new_image.id) 89 | colored_status = colored(status[:15], color) 90 | write_out(cols.format(k[:50], v.id[:32], new_image.id[:32], colored_status)) 91 | 92 | def update(self): 93 | if self.args.debug: 94 | write_out(str(self.args)) 95 | 96 | if self.args.all and self.args.image is not None: 97 | raise ValueError("Cannot specify both --all and an image name") 98 | 99 | if self.args.all and self.args.force: 100 | raise ValueError("Cannot specify both --all and --force") 101 | 102 | if self.args.all and self.args.storage is None: 103 | raise ValueError("Please specify --storage") 104 | 105 | beu = BackendUtils() 106 | 107 | if self.args.all: 108 | be = beu.get_backend_from_string(self.args.storage) 109 | return self.update_all_images(be, self.args.debug) 110 | 111 | try: 112 | be, img_obj = beu.get_backend_and_image_obj(self.image, str_preferred_backend=self.args.storage or storage, required=True if self.args.storage else False) 113 | input_name = img_obj.input_name 114 | except ValueError: 115 | raise ValueError("{} not found locally. Unable to update".format(self.image)) 116 | 117 | be.update(input_name, debug=self.args.debug, force=self.args.force, image_object=img_obj) 118 | return 0 119 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Submitting patches 2 | ------------------ 3 | 4 | Submit a pull request against . 5 | 6 | Please look at "git log" and match the commit log style. 7 | 8 | Running the test suite 9 | ---------------------- 10 | 11 | For builds you can use an unprivileged user, but because the `atomic` 12 | command uses Docker, you will need to use `sudo` to run the test 13 | suite, i.e.: 14 | 15 | ``` 16 | sudo make test 17 | ``` 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | COPYING -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Installation directories. 2 | PREFIX ?= $(DESTDIR)/usr 3 | SYSCONFDIR ?= $(DESTDIR)/etc/sysconfig 4 | PROFILEDIR ?= $(DESTDIR)/etc/profile.d 5 | export PYTHON ?= /usr/bin/python 6 | export PYTHON3 ?= /usr/bin/python3 7 | PYLINT ?= $(PYTHON) -m pylint 8 | PYTHON3_PYLINT ?= $(PYTHON3) -m pylint 9 | GO_MD2MAN ?= /usr/bin/go-md2man 10 | GO ?= /usr/bin/go 11 | PYTHONSITELIB=$(shell $(PYTHON) -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(0))") 12 | VERSION=$(shell $(PYTHON) setup.py --version) 13 | export GOPATH = $(shell pwd)/godeps 14 | BOX="fedora_atomic" 15 | 16 | .PHONY: all 17 | all: python-build docs pylint-check gotar 18 | 19 | .PHONY: test-python3-pylint 20 | test-python3-pylint: 21 | $(PYTHON3_PYLINT) --disable=all --enable=E --enable=W --additional-builtins=_ *.py atomic Atomic tests/unit/*.py -d=no-absolute-import,print-statement,no-absolute-import,bad-builtin,catching-non-exception,raising-non-exception 22 | 23 | .PHONY: test check test-suite 24 | 25 | check: test 26 | 27 | test-suite: 28 | ./test.sh 29 | 30 | test: all test-python3-pylint test-suite 31 | 32 | test-destructive: all 33 | ENABLE_DESTRUCTIVE=1 ./test.sh 34 | 35 | .PHONY: python-build 36 | python-build: 37 | $(PYTHON) setup.py build 38 | 39 | .PHONY: pylint-check 40 | pylint-check: 41 | $(PYLINT) --disable=all --enable=E --enable=W --additional-builtins=_ *.py atomic Atomic tests/unit/*.py -d=no-absolute-import,print-statement,no-absolute-import,bad-builtin,catching-non-exception,raising-non-exception 42 | 43 | MANPAGES_MD = $(wildcard docs/*.md) 44 | 45 | docs/%.1: docs/%.1.md 46 | $(GO_MD2MAN) -in $< -out $@.tmp && touch $@.tmp && mv $@.tmp $@ 47 | 48 | .PHONY: docs 49 | docs: $(MANPAGES_MD:%.md=%) 50 | 51 | gotar: gotar.go 52 | $(GO) build -o $@ $< 53 | 54 | .PHONY: clean 55 | clean: 56 | $(PYTHON) setup.py clean 57 | -rm -rf dist build *~ \#* *pyc .#* docs/*.1 58 | rm -fr build 59 | 60 | .PHONY: install-only 61 | install-only: 62 | $(PYTHON) setup.py install --prefix=/usr --install-scripts /usr/share/atomic `test -n "$(DESTDIR)" && echo --root $(DESTDIR)` 63 | 64 | (cd $(DESTDIR)/$(PYTHONSITELIB) && rm -f atomic-$(VERSION)-*egg-info) 65 | 66 | install -d -m 0755 $(DESTDIR)/usr/bin 67 | ln -fs ../share/atomic/atomic $(DESTDIR)/usr/bin/atomic 68 | 69 | install -d -m 0755 $(DESTDIR)/usr/libexec/atomic 70 | install -m 0755 migrate.sh gotar $(DESTDIR)/usr/libexec/atomic 71 | 72 | [ -d $(SYSCONFDIR) ] || mkdir -p $(SYSCONFDIR) 73 | install -m 644 atomic.sysconfig $(SYSCONFDIR)/atomic 74 | 75 | [ -d $(PROFILEDIR) ] || mkdir -p $(PROFILEDIR) 76 | install -m 644 atomic.sh $(PROFILEDIR) 77 | 78 | install -d $(PREFIX)/share/man/man1 79 | install -m 644 $(basename $(MANPAGES_MD)) $(PREFIX)/share/man/man1 80 | 81 | echo ".so man1/atomic-push.1" > $(PREFIX)/share/man/man1/atomic-upload.1 82 | 83 | install -m 644 atomic.conf $(DESTDIR)/etc 84 | 85 | install -d $(DESTDIR)/etc/atomic.d 86 | 87 | .PHONY: install 88 | install: all install-only 89 | 90 | 91 | .PHONY: install-openscap 92 | install-openscap: 93 | install -m 644 atomic.d/openscap $(DESTDIR)/etc/atomic.d 94 | 95 | .PHONY: vagrant-check 96 | vagrant-check: 97 | BOX=$(BOX) sh ./vagrant.sh 98 | 99 | .PHONY: install-on-atomicos 100 | install-on-atomicos: 101 | NO_TEST=1 sh ./.papr.sh 102 | -------------------------------------------------------------------------------- /README-atomic-scan.md: -------------------------------------------------------------------------------- 1 | % Brent Baude 2 | % August 2016 3 | 4 | # Atomic Scan JSON specification 5 | 6 | When creating a custom scanner plug-in, the JSON needs to be formatted in a specific 7 | way for Atomic to be able to output a summary for the user. 8 | 9 | Each JSON output file should have the following basic key and value pairs: 10 | 11 | ``` 12 | { 13 | "Scanner": "", # String name 14 | "Time": "", # Time stamp 15 | "Scan Type": "" # String name 16 | "Finished Time": "", # Time stamp 17 | "UUID": "", 18 | "Successful": "" # String bool (true/false) 19 | 20 | ... 21 | ``` 22 | At this level in the JSON, you can also inject custom information within a "Custom" key. For example: 23 | 24 | ``` 25 | "Custom": { 26 | "scanner_url": "http://foobar" 27 | ... 28 | } 29 | ``` 30 | 31 | Atomic scan will then look for one of two additional keys: Vulnerabilities or Results. If that key 32 | is present, it will then iterate recursively through the tree. An actual example from the openscap 33 | scanner looks like the following: 34 | 35 | ``` 36 | { 37 | "Scanner": "openscap", 38 | "Time": "2016-08-09T13:50:47", 39 | "CVE Feed Last Updated": "2016-05-31T03:16:12", 40 | "Scan Type": "CVE", 41 | "Finished Time": "2016-08-09T13:50:49", 42 | "UUID": "/scanin/53f20e902da704bc7efebf0c24e03ce1233cd364c5987ef895c9827fbc340474", 43 | "Successful": "true" 44 | "Vulnerabilities": [ 45 | { 46 | "Title": "RHSA-2016:1025: pcre security update (Important)", 47 | "Severity": "Important", 48 | "Description": "PCRE is a Perl-compatible regular expression library.\n\nSecurity Fix(es):\n\n* Multiple flaws were found in the way PCRE handled malformed regular expressions. An attacker able to make an application using PCRE process a specially crafted regular expression could use these flaws to cause the application to crash or, possibly, execute arbitrary code. (CVE-2015-8385, CVE-2016-3191, CVE-2015-2328, CVE-2015-3217, CVE-2015-5073, CVE-2015-8388, CVE-2015-8391, CVE-2015-8386)", 49 | "Custom": { 50 | 51 | ``` 52 | 53 | If you do not want to output anything to the user, then simply do not use the Results or Vulnerabilities 54 | keys. 55 | 56 | In the case that you want some simple output for the user, you can use the Custom key at the top 57 | top level (as shown above). This will be shown to the user. A good example would be if your 58 | scanner pushes the results to a web site, you could list that. 59 | 60 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure(2) do |config| 9 | config.vm.provider "libvirt" do |libvirt, override| 10 | libvirt.memory = 2048 11 | libvirt.cpus = 3 12 | libvirt.storage :file, 13 | :type => 'qcow2' 14 | end 15 | config.vm.synced_folder ".", "/vagrant", disabled: true 16 | config.vm.synced_folder ".", "/home/vagrant/atomic", type: "rsync", 17 | rsync__exclude: ".tmp*" 18 | 19 | # The most common configuration options are documented and commented below. 20 | # For a complete reference, please see the online documentation at 21 | # https://docs.vagrantup.com. 22 | 23 | # Every Vagrant development environment requires a box. You can search for 24 | # boxes at https://atlas.hashicorp.com/search. 25 | config.vm.define "fedora_atomic" do |fedora_atomic| 26 | fedora_atomic.vm.box = "fedora_atomic" 27 | fedora_atomic.vm.box_url = "https://getfedora.org/atomic_vagrant_libvirt_latest" 28 | end 29 | config.vm.define "centos_atomic" do |centos_atomic| 30 | centos_atomic.vm.box = "centos_atomic" 31 | centos_atomic.vm.box_url = "https://ci.centos.org/artifacts/sig-atomic/centos-continuous/images/cloud/latest/images/centos-atomic-host-7-vagrant-libvirt.box" 32 | end 33 | config.vm.define "fedora_cloud" do |fedora_cloud| 34 | fedora_cloud.vm.box = "fedora/28-cloud-base" 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /atomic-containers.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Giuseppe Scrivano 3 | % June 2016 4 | # NAME 5 | atomic-containers - operations on container 6 | 7 | # SYNOPSIS 8 | **atomic containers COMMAND [OPTIONS] [CONTAINERS...]** 9 | 10 | atomic images allows the user to view and operate on containers 11 | 12 | # COMMANDS 13 | **list** 14 | By default, will list all running containers on your system. 15 | 16 | Using --all will list all the installed containers. 17 | 18 | 19 | # list OPTIONS 20 | [**-h|--help**] 21 | [**-a|--all**] 22 | [**-f|--filter**] 23 | [**--json**] 24 | [**-n|--noheading**] 25 | [**--no-trunc**] 26 | [**-q|--quiet**] 27 | 28 | # OPTIONS: 29 | **-h** **--help** 30 | Print usage statement 31 | 32 | **-a** **--all** 33 | Print all the installed containers 34 | 35 | **-f** **--filter** 36 | Filter output based on given filters, example usage: `--filter id=foo` will list all containers that has "foo" as part of their ID. 37 | 38 | Filterables: `container (id)`, `image`, `command`, `created`, `status`, `runtime` 39 | 40 | **--json** 41 | Print in a machine parsable format 42 | 43 | **-n** **--noheading** 44 | Do not print heading when listing the containers 45 | 46 | **--no-trunc** 47 | Do not truncate output 48 | 49 | **-q** **--quiet** 50 | Only display container IDs 51 | 52 | # HISTORY 53 | June 2016, Originally compiled by Giuseppe Scrivano (gscrivan at redhat dot com) 54 | July 2016, Added sub-commands filter, no-trunc and quiet (jerzhang at redhat dot com) 55 | -------------------------------------------------------------------------------- /atomic.conf: -------------------------------------------------------------------------------- 1 | # Atomic CLI configuration file 2 | 3 | default_scanner: 4 | default_docker: docker 5 | registry_confdir: /etc/containers/registries.d/ 6 | discover_sigstores: true 7 | sigstore_metadata_image: sigstore 8 | 9 | 10 | # Default storage backend [ostree, docker] 11 | # default_storage: docker 12 | # ostree_repository: /ostree/repo 13 | # checkout_path: /var/lib/containers/atomic 14 | # 15 | 16 | # Default identity for signing images 17 | # default_signer: 18 | # Absolute path to GPG keyring. Value set as environment variable GNUPGHOME 19 | #gnupg_homedir: /home/USER/.gnupg 20 | # 21 | # To always use a proxy with atomic, you can uncomment and fill out 22 | # below. 23 | # 24 | #http_proxy: 25 | #https_proxy: 26 | #no_proxy: 27 | -------------------------------------------------------------------------------- /atomic.d/openscap: -------------------------------------------------------------------------------- 1 | type: scanner 2 | scanner_name: openscap 3 | image_name: registry.access.redhat.com/rhel7/openscap 4 | default_scan: cve 5 | custom_args: ['-v', '/etc/oscapd:/etc/oscapd:ro'] 6 | scans: [ 7 | { name: cve, 8 | args: ['oscapd-evaluate', 'scan', '--no-standard-compliance', '--targets', 'chroots-in-dir:///scanin', '--output', '/scanout', '-j1'], 9 | description: "Performs a CVE scan based on Red Hat relesead CVE OVAL. !WARNING! This CVE is built into container image and it might be out-of-date. Change config.ini to configure the scanner to fetch latest CVE data"}, 10 | { name: standards_compliance, 11 | args: ['oscapd-evaluate', 'scan', '--targets', 'chroots-in-dir:///scanin', '--output', '/scanout', '--no-cve-scan', '-j1'], 12 | description: "!DEPRECATED! Performs scan with Standard Profile, as present in SCAP Security Guide shipped in Red Hat Enterprise Linux" 13 | }, 14 | { name: configuration_compliance, 15 | args: ['oscapd-evaluate', 'scan', '--targets', 'chroots-in-dir:///scanin', '--output', '/scanout', '--no-cve-scan', '-j1'], 16 | description: "Performs a configuration compliance scan according to selected profile from SCAP Security Guide shipped in Red Hat Enterprise Linux." 17 | } 18 | ] 19 | 20 | -------------------------------------------------------------------------------- /atomic.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed under the GNU General Public License Version 2 3 | # This program is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 2 of the License, or 6 | # (at your option) any later version. 7 | 8 | command_not_found_handle () { 9 | local runcnf=1 10 | local retval=127 11 | 12 | [ -f /etc/sysconfig/atomic ] && . /etc/sysconfig/atomic 13 | 14 | # only search for the command if we're interactive 15 | [[ $- =~ i ]] || runcnf=0 16 | 17 | # don't run if not on an atomic host or tools 18 | ([ -n "${TOOLSIMG}" ] && [ -f /run/ostree-booted ] && [ -x /usr/bin/atomic ]) || runcnf=0 19 | 20 | # run the command, or just print a warning 21 | if [ $runcnf -eq 1 ]; then 22 | echo "Redirecting to container ${TOOLSIMG}..." 23 | atomic run ${TOOLSIMG} "$@" 24 | retval=$? 25 | else 26 | echo "bash: $1: command not found" 27 | fi 28 | 29 | # return success or failure 30 | return $retval 31 | } 32 | 33 | -------------------------------------------------------------------------------- /atomic.sysconfig: -------------------------------------------------------------------------------- 1 | # Specify the tools package to be used for missing commands 2 | # A missing command on an atomic host platform will execute 3 | # atomic run ${TOOLSIMG} COMMAND 4 | # If the TOOLSIMG is defined 5 | # export TOOLSIMG= 6 | -------------------------------------------------------------------------------- /atomicdesign.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectatomic/atomic/d5f3f19c4f18b24d5ccf47a10d39dbc99af4697a/atomicdesign.pdf -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | *.1 2 | -------------------------------------------------------------------------------- /docs/atomic-containers.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Giuseppe Scrivano 3 | % June 2016 4 | # NAME 5 | atomic-containers - operations on containers 6 | 7 | # SYNOPSIS 8 | **atomic containers COMMAND [OPTIONS] [CONTAINERS...]** 9 | 10 | atomic containers allows the user to view and operate on containers 11 | 12 | # COMMANDS 13 | **delete** 14 | 15 | delete specified container(s). 16 | 17 | **list** 18 | 19 | list containers on your system. 20 | 21 | **trim** 22 | 23 | discard unused blocks (fstrim) on running containers. 24 | 25 | **update** 26 | 27 | update a system container. 28 | 29 | **rollback** 30 | 31 | rollback a system container. 32 | 33 | # DESCRIPTION 34 | **atomic containers delete**, delete specified container image from the system. 35 | 36 | Using --all will delete all the installed containers. 37 | 38 | **atomic containers list**, by default, will list all running containers on your 39 | system. 40 | 41 | Using --all will list all the installed containers. 42 | 43 | **atomic containers trim**, Discard unused blocks (fstrim) on rootfs of running containers. 44 | 45 | e.g. If you have 2 running containers on your system with container IDs (496b8679b6cf, 9bb990da1203). 46 | 47 | >atomic containers trim 48 | Trimming container id 496b8679b6cf 49 | Trimming container id 9bb990da1203 50 | 51 | **atomic containers update**, update a system container to use a newer version of an image. 52 | 53 | Can use --set to update environment variables. 54 | 55 | **atomic containers rollback**, rollback a system container to the other deployment if one exists. 56 | 57 | # OPTIONS: 58 | **-h** **--help** 59 | Print usage statement 60 | 61 | # delete OPTIONS: 62 | **-a** **--all** 63 | Delete all the installed containers 64 | 65 | **-f** **--force** 66 | Force the deletion of specified running containers 67 | 68 | # list OPTIONS: 69 | **-a** **--all** 70 | Print all the installed containers 71 | 72 | **-f** **--filter** 73 | Filter output based on given filters, example usage: `--filter container=foo` will list all containers that has "foo" as part of their container ID. 74 | 75 | Filterables: `backend`, `command`, `container`, `created`, `image`, `runtime`, `state` 76 | 77 | **--json** 78 | Print in a machine parsable format 79 | 80 | **-n** **--noheading** 81 | Do not print heading when listing the containers 82 | 83 | **--no-trunc** 84 | Do not truncate output 85 | 86 | **-q** **--quiet** 87 | Only display container IDs 88 | 89 | # update OPTIONS: 90 | **--rebase=IMAGE** 91 | Rebase to a different image. If not specified, the same image used to install the container will be used. 92 | 93 | **-a** **--all** 94 | Update all the installed containers. If any update fails, then it rollbacks automatically to the working version of the container. 95 | 96 | **--set=NAME=VALUE** 97 | Set a value that is going to be used by a system container for its configuration and can be specified multiple times. OSTree is required for this feature to be available. 98 | 99 | # HISTORY 100 | June 2016, Originally compiled by Giuseppe Scrivano (gscrivan at redhat dot com) 101 | July 2016, Added sub-commands filter, no-trunc and quiet (jerzhang at redhat dot com) 102 | Sept 2016, Added atomic containers trim subcommand (shishir dot mahajan at redhat dot com) 103 | -------------------------------------------------------------------------------- /docs/atomic-diff.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Brent Baude 3 | % November 2015 4 | # NAME 5 | atomic-diff - show the differences between two images|containers RPMs 6 | # SYNOPSIS 7 | **atomic diff** 8 | [**-h**|**--help**] 9 | [**--json**] 10 | [**--names-only**] 11 | [**-n**][**--no-files**] 12 | [**-r**][**--rpms**] 13 | [**-v**][**--verbose**] 14 | image|container image|container ...] 15 | 16 | # DESCRIPTION 17 | **atomic diff** will compare the files found in two different images or containers 18 | and output to stdout or as JSON. By default, the comparison is done on the file level 19 | but there are switches for comparing RPMs and metadata as well. 20 | 21 | # OPTIONS 22 | **-h** **--help** 23 | Print usage statement. 24 | 25 | **--json** 26 | Output in the form of JSON. 27 | 28 | **-k** **--keywords** 29 | Use the following keywords for comparison of files. You must select at least one but multiple can 30 | be used as well. Keywords that are used for this option are exclusive, which means that any only those 31 | keywords will be used. 32 | 33 | Keywords currently defined are: **all**, **link**, **nlink**, **mode**, **type**, **time**, **uid**, **gid**, **size**, **sha256digest** 34 | 35 | **-m** **--metadata** 36 | Show the differences in the metadata for the two images or containers. 37 | 38 | **-n** **--no-files** 39 | Do not perform a file based diff between the two images or containers. Often used 40 | when performing an RPM-based diff to restrict output. 41 | 42 | **--names-only** 43 | When performing the diff, only compare package names and not their versions. 44 | 45 | **-r** **--rpms** 46 | Show the where the two docker objects have different RPMs. 47 | 48 | **-v** **--verbose** 49 | Be verbose in showing the differences in RPMs. The default will only show the differences in RPMs, whereas 50 | with **verbose** it will show all the RPMS in each object. 51 | 52 | 53 | 54 | # EXAMPLES 55 | Compare images the files in 'foo1' and 'foo2'. 56 | 57 | atomic diff foo1 foo2 58 | 59 | Compare the files in images 'foo1' and 'foo2' and output in JSON. 60 | 61 | atomic diff --json foo1 foo2 62 | 63 | Compare only the RPMs in images 'foo1' and 'foo2' 64 | 65 | atomic diff -r -n foo1 foo2 66 | 67 | Compare the files and RPMs (without versions) in images 'foo1' and 'foo2' and output as json 68 | 69 | atomic diff -r --json foo1 foo2 70 | 71 | Compare only the metadata between images 'foo1' and 'foo2' 72 | 73 | atomic diff -m foo1 foo2 74 | 75 | Compare files by 'sha256digests' and 'time' between images 'foo1' and 'foo2' 76 | 77 | atomic diff foo1 foo2 --keywords sha256digest time 78 | 79 | # HISTORY 80 | Updated by Brent Baude (bbaude at redhat dot com) Nov 2016 81 | Updated by Brent Baude (bbaude at redhat dot com) May 2016 82 | Initial revision by Brent Baude (bbaude at redhat dot com) November 2015 83 | -------------------------------------------------------------------------------- /docs/atomic-help.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Tomas Tomecek 3 | % May 2017 4 | # NAME 5 | atomic-help - Get help for container images 6 | 7 | # SYNOPSIS 8 | **atomic help** 9 | [**-h**|**--help**] 10 | IMAGE 11 | 12 | **atomic help** provides documentation for the specified container IMAGE. 13 | 14 | The documentation is extracted from the container image. **atomic** searches 15 | for the documentation in following locations: 16 | 17 | 1. File placed inside root of the container named either **help.1** or 18 | **README.md**. 19 | 2. Label named **help** which should contain executable command 20 | to display the documentation. 21 | 22 | # OPTIONS 23 | **-h** **-help** 24 | Print usage statement. 25 | 26 | # HISTORY 27 | May 2017, Originally compiled by Tomas Tomecek (ttomecek at redhat dot com) 28 | -------------------------------------------------------------------------------- /docs/atomic-host.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Dan Walsh 3 | % January 2015 4 | # NAME 5 | atomic-host - Manage Atomic Host Commands 6 | 7 | # SYNOPSIS 8 | **atomic host [OPTIONS] COMMAND** 9 | 10 | This command is a high-level wrapper for the underlying `rpm-ostree` tool which 11 | can perform upgrades, rollbacks, and system state inspection. It is used 12 | for implementations of the Project Atomic Host pattern. 13 | 14 | #NOTE 15 | The `host` subcommand is only available on `Atomic Host Systems`. 16 | 17 | # OPTIONS 18 | **-h** **-help** 19 | Print usage statement 20 | 21 | **-r** **--reboot** 22 | Initiate a reboot after rollback is prepared. 23 | 24 | # COMMANDS 25 | **status** 26 | List information about all deployments 27 | 28 | **rollback** 29 | Switch to alternate installed tree at next boot 30 | 31 | **upgrade** 32 | Upgrade to the latest Atomic tree if one is available 33 | 34 | **deploy** 35 | Download and deploy a specific Atomic tree 36 | 37 | **unlock** 38 | Remove the read-only bind mount on `/usr` 39 | and replace it with a writable overlay filesystem. This 40 | default invocation of "unlock" is intended for 41 | development/testing purposes. All changes in the overlay 42 | are lost on reboot (or upgrade). Pass `--hotfix` to create changes 43 | that persist on reboot (but still not upgrades). 44 | 45 | 46 | # SEE ALSO 47 | man rpm-ostree 48 | man ostree 49 | 50 | # HISTORY 51 | January 2015, Originally compiled by Daniel Walsh (dwalsh at redhat dot com) 52 | -------------------------------------------------------------------------------- /docs/atomic-mount.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Will Temple 3 | % June 2015 4 | # NAME 5 | atomic-mount - Mount Images/Containers to Filesystem 6 | 7 | # SYNOPSIS 8 | **atomic mount** 9 | [**--live** | **--shared** | **--storage=[ostree|docker]** | [**-o**|**--options** *OPTIONS*]] 10 | [REGISTRY/]REPO[:TAG]|UUID|NAME 11 | DIRECTORY 12 | 13 | # DESCRIPTION 14 | **atomic mount** attempts to mount the underlying filesystem of a container or 15 | image into the host filesystem. Accepts one of image UUID, container UUID, 16 | container NAME, or image REPO (optionally with registry and tag information). 17 | If the given UUID or NAME is a container, and **--live** is not set, then 18 | *atomic mount* will create a snapshot of the container by committing it to a 19 | temporary image and spawning a temporary container from that image. If UUID or 20 | REPO refers to an image, then *atomic mount* will simply create a temporary 21 | container from the given image. If the UID is not zero, i.e. not being run as 22 | root, then *atomic mount* will call ostree checkout with --user-mode option. 23 | It will also ignore the mount system call, since ostree checkout takes care 24 | of that. All temporary artifacts are cleaned upon 25 | *atomic unmount*. Atomic mount is *only* supported on the devicemapper and 26 | overlayfs docker storage backends. If an image stored on an OSTree 27 | repository is mounted, then a temporary checkout is done, which will 28 | be deleted by atomic unmount. 29 | 30 | # OPTIONS 31 | **-o|--options** *OPTIONS* 32 | Specify options to be passed to *mount*. All options accepted by the 'mount' 33 | command are valid. The default mount options for the devicemapper backend (if 34 | the **--live** flag is unset) are: 'ro,nodev,nosuid'. If the **-o** flag is 35 | specified, then no default options are assumed. Use of the 'rw' flag is 36 | discouraged, as writes into the atomic temporary containers are never 37 | preserved. Use of this option conflicts with **--live**, as live containers 38 | have predetermined, immutable mount options. The OverlayFS driver has, by 39 | default, only the 'ro' option set, and the 'rw' option is illegal and will 40 | cause the program to terminate. 41 | 42 | **--live** 43 | Mount a running container live, writable, and synchronized. This option allows 44 | the user to modify the container's contents as the container runs or update 45 | the container's software without rebuilding the container. If live mode is 46 | used, no mount options may be provided. Live mode is *not* supported on the OverlayFS docker storage driver. 47 | 48 | **--shared** 49 | Mount a container image with a shared SELinux label 50 | 51 | [**--storage=[ostree|docker]**] 52 | Optionally specify the storage of the image. Will prompt user to specify if 53 | the same image name exists in both ostree and docker, and the user did not specify. 54 | 55 | # HISTORY 56 | June 2015, Originally compiled by William Temple (wtemple at redhat dot com) 57 | -------------------------------------------------------------------------------- /docs/atomic-pull.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Giuseppe Scrivano 3 | % April 2016 4 | # NAME 5 | atomic-pull - fetch an image locally 6 | 7 | # SYNOPSIS 8 | **atomic pull** 9 | [**-h|--help**] 10 | [**--storage=[ostree|docker]**] 11 | [**-t**|**--type** atomic] 12 | IMAGE 13 | 14 | # DESCRIPTION 15 | **atomic pull**, will fetch a remote image and store it locally. 16 | 17 | You can pull an image from a docker registry (like docker.io) to your 18 | local docker daemon with atomic pull. 19 | 20 | `atomic pull docker.io/busybox:latest` 21 | 22 | Use the `--storage ostree` option to store it into the OSTree repository. You can 23 | define a default storage type in **/etc/atomic.conf** with the key of 24 | **default_storage**. 25 | 26 | IMAGE has the form `SOURCE:IMAGE-NAME`, where `SOURCE` can be one of 27 | 'oci', 'docker', 'dockertar', 'ostree', 'http'. If no `SOURCE` is 28 | specified then 'oci' is assumed. 29 | 30 | An 'oci' image is fetched via Skopeo from a Docker registry. These 31 | two commands are equivalent: 32 | 33 | `atomic pull etcd` 34 | `atomic pull oci:etcd` 35 | 36 | A 'docker' image is imported from the local Docker engine, thus not 37 | accessing the network. It is equivalent to saving the image from 38 | docker (`docker save IMAGE`) and importing it into the OSTree 39 | repository: 40 | 41 | `atomic pull --storage ostree docker:fedora:latest` 42 | 43 | A 'dockertar' image works in a similar way to 'docker' images, except 44 | that the saved tarball is specified: 45 | 46 | `atomic pull --storage ostree dockertar:/path/to/the/image.tar` 47 | 48 | If the user is not privileged, the image will be stored in the user 49 | specific repository. 50 | 51 | If you are pulling from an insecure registry, use the 'http' prefix. 52 | It tells Skopeo to not do TLS verification on the specified registry. 53 | 54 | `atomic pull --storage ostree http:REGISTRY/IMAGE:TAG` 55 | 56 | Images where the registry is not specified are supported 57 | when pulling to 'ostree'. However, we recommend that you use a 58 | fully qualified name to refer unambiguously to the image. 59 | 60 | If your /etc/containers/policy.json requires signature verification, the 61 | pulled image is verified prior to being made available to the local docker 62 | daemon. When interacting with a docker registry, Atomic uses the policy 63 | and YAML configuration files /etc/containers/ to determine: 64 | 65 | * if the image should be verified with a signature 66 | * and where to get the signature 67 | 68 | If you use the `--type atomic` switch to interact with an atomic registry, 69 | Atomic will still use the policy to determine if verification is needed. The 70 | signature itself will be obtained from the atomic registry. An example of 71 | pulling from an atomic registry could be: 72 | 73 | `atomic pull --type atomic my-atomic-registry:images/foobar` 74 | 75 | # OPTIONS: 76 | **-h** **--help** 77 | Print usage statement 78 | 79 | **--src-creds=USERNAME[:PASSWORD]** 80 | Define the credentials to use with the source registry. 81 | 82 | **--storage=[ostree|docker]** 83 | Define the destination storage for the pulled image. 84 | 85 | **-t** **--type atomic** 86 | Define an alternate registry type. The only valid option is **atomic** for 87 | when you want to take advantage of advanced atomic registry options. 88 | 89 | # HISTORY 90 | April 2016, Originally compiled by Giuseppe Scrivano (gscrivan at 91 | redhat dot com) 92 | -------------------------------------------------------------------------------- /docs/atomic-push.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Dan Walsh 3 | % September 2015 4 | # NAME 5 | atomic-push - push Image to repository 6 | 7 | # SYNOPSIS 8 | **atomic push** 9 | [**-a**][**--activation_key**[=*ACTIVATION_KEY*]] 10 | [**--anonymous**] 11 | [**--debug**] 12 | [**-h**|**--help**] 13 | [**--insecure**] 14 | [**--pulp**] 15 | [**-p**][**--password**[=*PASSWORD*]] 16 | [**-r**][**--repository_id**[=*REPOSITORY_ID*]] 17 | [**--satellite**] 18 | [**--sign-by**] 19 | [**--t**][**--type**] 20 | [**-u**][**--username**[=*USERNAME*]] 21 | [**-U**][**--url**[=*URL*]] 22 | [**--verify_ssl**[=*VERIFY_SSL*]] 23 | 24 | # DESCRIPTION 25 | **atomic push** will push the image to the repository. Defaults to docker repository; can also upload to satellite or pulp repository. 26 | 27 | # OPTIONS: 28 | **-a ACTIVATION_KEY** **--activation_key ACTIVATION_KEY** 29 | Activation Key 30 | 31 | **--anonymous** 32 | Push without a username or password 33 | 34 | **--debug** 35 | Debug mode 36 | 37 | **-h** **--help** 38 | Print usage statement 39 | 40 | **--insecure** 41 | Indicate that the regsitry does not require HTTPS or certificate verification. 42 | 43 | **-p PASSWORD** **--password PASSWORD** 44 | Password for remote registry 45 | 46 | **--pulp** 47 | Push using the pulp protocol, defaults to using docker push 48 | 49 | **--r REPO_ID** **--repository_id REPO_ID** 50 | Repository ID 51 | 52 | **--satellite** 53 | Upload using the satellite protocol; defaults to using docker push 54 | 55 | **--sign-by** 56 | Override the default signing identity defined in /etc/atomic.conf. Atomic push will always sign if there is a default 57 | identity or you pass an indentity here. If there is a default identity, you can pass **None** to **--sign-by** and 58 | signing will be disabled. 59 | 60 | **-t REGISTRY_TYPE** **--type REGISTRY_TYPE** 61 | Change the registry type, **docker|atomic**. atomic registry type is an OpenShift-based registry with an API supporting image signatures. Default is **docker**. 62 | 63 | **-u USERNAME** **--username USERNAME** 64 | Username for remote registry 65 | 66 | **-U URL** **--url URL** 67 | URL for remote registry 68 | 69 | **--verify_ssl** 70 | Flag to verify ssl of registry 71 | 72 | # HISTORY 73 | April 2015, Originally compiled by Daniel Walsh (dwalsh at redhat dot com) 74 | 75 | July 2015, Edited by Jenny Ramseyer (jramseye at redhat dot com) 76 | 77 | September 2015, Edited by Daniel Walsh (dwalsh at redhat dot com) 78 | 79 | September 2016, Updated by Brent Baude (bbaude at redhat dot com) 80 | -------------------------------------------------------------------------------- /docs/atomic-run.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Dan Walsh 3 | % January 2015 4 | # NAME 5 | atomic-run - Execute container image run method 6 | 7 | # SYNOPSIS 8 | **atomic run** 9 | [**-h**|**--help**] 10 | [**--display**] 11 | [**-n**][**--name**[=*NAME*]] 12 | [**-r**, **--replace**] 13 | [**--spc**] 14 | [**--storage**] 15 | [**--set**=*NAME*=*VALUE*] 16 | [**--quiet**] 17 | IMAGE [COMMAND] [ARG...] 18 | 19 | # DESCRIPTION 20 | **atomic run** attempts to start an existing container or run a container 21 | from an image, first reading the `LABEL RUN` field in the container IMAGE. 22 | 23 | 24 | If the container image has a LABEL RUN instruction like the following: 25 | 26 | `LABEL RUN /usr/bin/docker run -t -i --rm \${OPT1} --cap-add=SYS_ADMIN --net=host -v \${LOGDIR}:/var/log -v \${DATADIR}:/var/lib --name \${NAME} \${IMAGE} \${OPT2} run.sh \${OPT3}` 27 | 28 | `atomic run` will run the following: 29 | 30 | `/usr/bin/docker run -t -i --rm --cap-add=SYS_ADMIN --net=host -v ${LOGDIR}:/var/log -v ${DATADIR}:/var/lib --name ${NAME} ${IMAGE} run.sh` 31 | 32 | If this field does not exist, `atomic run` defaults to the following: 33 | 34 | `/usr/bin/docker run -t -i --rm -v ${LOGDIR}:/var/log -v ${DATADIR}:/var/lib --name ${NAME} ${IMAGE}` 35 | 36 | These defaults are suggested values for your container images. 37 | 38 | `atomic run` will set the following environment variables for use in the command: 39 | 40 | **NAME** 41 | The name specified via the command. NAME will be replaced with IMAGE if it is not specified. 42 | 43 | **IMAGE** 44 | The name and image specified via the command. 45 | 46 | **OPT1, OPT2, OPT3** 47 | Additional options which can be specified via the command. 48 | 49 | **SUDO_UID** 50 | The `SUDO_UID` environment variable. This is useful with the docker `-u` option for user space tools. If the environment variable is not available, the value of `/proc/self/loginuid` is used. 51 | 52 | **SUDO_GID** 53 | The `SUDO_GID` environment variable. This is useful with the docker `-u` option for user space tools. If the environment variable is not available, the default GID of the value for `SUDO_UID` is used. If this value is not available, the value of `/proc/self/loginuid` is used. 54 | 55 | **RUN_OPTS** 56 | Content of file specified by `LABEL RUN_OPTS_FILE`. During `atomic install`, the `install.sh` can populate the file with any additional options that need to be passed to `docker run`, for example `--hostname=www.example.test` or `--net host`. The file name undergoes environment variable expansion, so for example `LABEL RUN_OPTS_FILE '/var/lib/${NAME}/docker-run-opts'` can be used to store per-container configuration. 57 | 58 | Custom environment variables can be provided to the container through the LABEL RUN instruction as follows: 59 | 60 | `LABEL RUN /usr/bin/docker run -t -i --rm -e FOO="\${FOO:-bar}" -v \${LOGDIR}:/var/log -v \${DATADIR}:/var/lib --name \${NAME} \${IMAGE}` 61 | 62 | `atomic run` will run the following: 63 | 64 | `/usr/bin/docker run -t -i --rm -e FOO="${FOO:-bar}" -v ${LOGDIR}:/var/log -v ${DATADIR}:/var/lib --name ${NAME} ${IMAGE}` 65 | 66 | The value of `FOO` can be set explicitly via `FOO=baz atomic run`. 67 | 68 | # OPTIONS: 69 | **-h** **--help** 70 | Print usage statement 71 | 72 | **--display** 73 | Display the image's run options and environment variables populated into the run command. 74 | The run command will not execute if --display is specified. 75 | If --display is not specified the run command will execute. 76 | 77 | **--n** **--name**="" 78 | Use this name for creating run content for the container. 79 | NAME will default to the IMAGENAME if it is not specified. 80 | 81 | **-r** **--replace** 82 | Replaces an existing container by the same name if it exists prior to running. 83 | 84 | **--runtime=PATH** 85 | Change the OCI runtime used by the systemd service file for running 86 | system containers and user containers. If runtime is not defined, the 87 | value **runtime** in the configuration file is used for system 88 | containers. If there is no runtime defined in the configuration file 89 | as well, then the default **/usr/bin/runc** is used. 90 | 91 | **--spc** 92 | Run container in super privileged container mode. The image will run with the following command: 93 | 94 | `/usr/bin/docker run -t -i --rm --privileged -v /:/host -v /run:/run --net=host --ipc=host --pid=host -e HOST=/host -e NAME=${NAME} -e IMAGE=${IMAGE} --name ${NAME} ${IMAGE}` 95 | 96 | **--storage** 97 | Allows you to override the default definition for the storage backend where your image will reside if pulled. If the image is already local, 98 | the --storage option will dictate where atomic should look for the image prior to running. Valid options are `docker` and `ostree`. 99 | 100 | **--set=NAME=VALUE** 101 | Set a value that is going to be used by a system container for its 102 | configuration and can be specified multiple times. It is used only 103 | by --system. OSTree is required for this feature to be available. 104 | 105 | 106 | **--quiet** 107 | Run without verbose messaging (i.e. security warnings). 108 | 109 | # HISTORY 110 | January 2015, Originally compiled by Daniel Walsh (dwalsh at redhat dot com) 111 | July 2015, edited by Sally O'Malley (somalley at redhat dot com) 112 | -------------------------------------------------------------------------------- /docs/atomic-scan.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Brent Baude 3 | % September 2015 4 | # NAME 5 | atomic-scan - Scan for CVEs in a container or image 6 | # SYNOPSIS 7 | **atomic scan** 8 | [**-h**|**--help**] 9 | [**--list**] 10 | [**--scanner**] 11 | [**--scan_type**] 12 | [**--verbose**] 13 | [**--all** | **--images** | **--containers** | **--rootfs** rootfs path to scan| 14 | IMAGE or CONTAINER names ...] 15 | 16 | # DESCRIPTION 17 | **atomic scan** will scan the a container or image looking for known Common Vulnerabilities and Exposures(CVEs) by default. It can also scan 18 | paths on the host filesystem as well using the _--rootfs_ option. 19 | 20 | The architecture for _atomic scan_ is very plug-in friendly. You can define additional scanners to use via the plug-in interface. To list the 21 | available scanners setup on your system, you can use _--list_. To use a different scanner, you simple pass its name with the _--scanner_ switch. 22 | You can also select a different scan type using the _--scan_type_ switch. 23 | 24 | 25 | # OPTIONS 26 | **-h** **--help** 27 | Print usage statement 28 | 29 | **--verbose** 30 | Show more verbose output. Specifically the stdout from the image scanner itself. 31 | 32 | **--list** 33 | Show all scanners configured for atomic and their scan types. 34 | 35 | **--scanner** 36 | Select as scanner other than the default. 37 | 38 | **--scan_type** 39 | Select a scan_type other than the default. 40 | 41 | **--scanner_args** 42 | Provide additional arguments for the scanner, for example specify a compliance profile. 43 | 44 | **--all** 45 | Instead of providing image or container names, scan all images (excluding intermediate image layers) and containers 46 | 47 | **--images** 48 | Scan all images (excluding intermediate layers). Similar to the results of `docker images`. 49 | 50 | **--containers** 51 | Scan all containers. Similar to the results of `docker ps -a` 52 | 53 | **--rootfs** 54 | Rootfs path to scan. Can provide _--rootfs_ multiple times. 55 | Note: SELinux separation will be disabled for --rootfs scans, but all other container 56 | separation will still be in place. 57 | 58 | **--remediate** 59 | Allows the scanner to run a remediation script when scanning is complete. The remediation script is provided 60 | by the scanner itself. 61 | 62 | **Note:** not all scanners provide remediation scripts. 63 | 64 | # EXAMPLES 65 | List all the scanners atomic knows about and display their default scan types. 66 | 67 | atomic scan --list 68 | 69 | Scan an image named 'foo1'. 70 | 71 | atomic scan foo1 72 | 73 | 74 | Scan and remediate an image named 'foo1'. 75 | 76 | atomic scan --remediate foo1 77 | 78 | Scan images named 'foo1' and 'foo2' and produce a detailed report. 79 | 80 | atomic scan foo1 foo2 81 | 82 | Scan all containers. 83 | 84 | atomic scan --containers 85 | 86 | Scan all containers and images and create a detailed report. 87 | 88 | atomic scan --all 89 | 90 | Scan a rootfs mounted at /tmp/chroot 91 | 92 | atomic scan --rootfs /tmp/chroot 93 | 94 | Scan an image called 'foo1' with a scanner called 'custom_scanner' and its default scan_type 95 | 96 | atomic scan --scanner custom_scanner foo1 97 | 98 | Scan an image called 'foo1' with a scanner called 'custom_scanner' and a scan type of 'list_rpms' 99 | 100 | atomic scan --scanner custom_scanner --scan_type list_rpms foo1 101 | 102 | # HISTORY 103 | Initial revision by Brent Baude (bbaude at redhat dot com) September 2015 104 | Updated for new atomic scan architecture by Brent Baude (bbaude at redhat dot com) May 2016 105 | -------------------------------------------------------------------------------- /docs/atomic-sign.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Brent Baude 3 | % August 2016 4 | # NAME 5 | atomic-sign- Create a signature for an image 6 | 7 | **WARNING** 8 | 9 | Only use **atomic sign** if you trust the remote registry which contains the image 10 | (preferably by being the only administrator of it). 11 | 12 | 13 | # SYNOPSIS 14 | **atomic sign** 15 | [**-h**|**--help**] 16 | [**-d**, **--directory**] 17 | [**--sign-by**] 18 | [**-g**, **--gnupghome**] 19 | [ image ... ] 20 | 21 | # DESCRIPTION 22 | **atomic sign** will create a local signature for one or more local images that have 23 | been pulled from a registry. By default, the signature will be written into a directory 24 | derived from the registry configuration files as configured by **registry_confdir** 25 | in /etc/atomic.conf. 26 | 27 | # OPTIONS 28 | **-h** **--help** 29 | Print usage statement. 30 | 31 | **-d** **--directory** 32 | Store the signatures in the specified directory. Default: /var/lib/atomic/signature 33 | 34 | **--sign-by** 35 | Override the default identity of the signature. You can define a default in /etc/atomic.conf 36 | with the key **default_signer**. 37 | 38 | **-g** **--gnupghome** 39 | Specify the GNUPGHOME directory to use for signing, e.g. ~/.gnupg. This 40 | argument will override the value of **gnupg_homedir** in /etc/atomic.conf. 41 | Defaults to the homedir or the uid defined in /proc/self/loginuid if it exists, or 42 | $SUDO_UID if it is defined, or current UID. 43 | 44 | # EXAMPLES 45 | Sign the foobar image from privateregistry.example.com 46 | 47 | atomic sign privateregistry.example.com/foobar 48 | 49 | Sign the foobar image and save the signature in /tmp/signatures/. 50 | 51 | atomic sign -d /tmp/signatures privateregistry.example.com 52 | 53 | Sign the busybox image with the identify of foo@bar.com with a user's keyring 54 | 55 | sudo atomic sign --sign-by foo@bar.com --gnupghome=~/.gnupg privateregistry.example.com 56 | 57 | # RELATED CONFIGURATION 58 | 59 | The write (and read) location for signatures is defined in YAML-based 60 | configuration files in /etc/containers/registries.d/. When you sign 61 | an image, atomic will use those configuration files to determine 62 | where to write the signature based on the the name of the originating 63 | registry or a default storage value unless overriden with the -d 64 | option. For example, consider the following configuration file. 65 | 66 | docker: 67 | privateregistry.example.com: 68 | sigstore: file:///var/lib/atomic/signature 69 | 70 | When signing an image preceeded with the registry name 'privateregistry.example.com', 71 | the signature will be written into subdirectories of 72 | /var/lib/atomic/signature/privateregistry.example.com. The use of 'sigstore' also means 73 | the signature will be 'read' from that same location on a pull-related function. 74 | 75 | You can also scope the registry definitions by repository and even name. Consider the 76 | following addition to the configuration above. 77 | 78 | privateregistry.exaple.com/john: 79 | sigstore-staging: file:///mnt/export/signatures 80 | sigstore: https://www.example.com/signatures/ 81 | 82 | Now any image from the john repository will use the sigstore-staging location of 83 | '/mnt/export/signatures'. Also note the use of sigstore-staging versus sigstore. This 84 | means that signatures should be written to that location but read should occur from 85 | the http URL provided. 86 | 87 | The user's keyring will be used during signing. When running as root user this may 88 | not be desired. Another keyring may be specified using environment variable GNUPGHOME, 89 | passed in via argument --gnupghome or set in configuration file atomic.conf. For example: 90 | 91 | gnupg_homedir: /home/USER/.gnupg 92 | 93 | # HISTORY 94 | Initial revision by Brent Baude (bbaude at redhat dot com) August 2016 95 | Updated by Brent Baude (bbaude at redhat dot com) September 2016 96 | Updated by Aaron Weitekamp (aweiteka at redhat dot com) September 2016 97 | -------------------------------------------------------------------------------- /docs/atomic-stop.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Dan Walsh 3 | % January 2015 4 | # NAME 5 | atomic-stop - Execute container image stop method 6 | 7 | # SYNOPSIS 8 | **atomic stop** 9 | [**--display**] 10 | [**-h**|**--help**] 11 | container [ARG...] 12 | 13 | # DESCRIPTION 14 | **atomic stop** attempts to stop a running container, first reading the 15 | `LABEL STOP` field in the container IMAGE. 16 | 17 | If the container image has a `LABEL STOP` instruction like the following: 18 | 19 | `LABEL STOP /usr/bin/docker kill -s HUP \${NAME}` 20 | 21 | atomic would execute this command before stopping the container. 22 | 23 | `atomic stop` will set the following environment variables for use in the command: 24 | 25 | If this field does not exist, `atomic stop` will just stop the container, if 26 | the container is running. 27 | 28 | Any additional arguments will be appended to the command. 29 | 30 | # OPTIONS: 31 | **--display** 32 | Display the container's stop options and environment variables populated into the stop command. 33 | The stop command will not execute if --display is specified. 34 | If --display is not specified the stop command will execute. 35 | **-h** **--help** 36 | Print usage statement 37 | 38 | # HISTORY 39 | March 2015, Originally compiled by Daniel Walsh (dwalsh at redhat dot com) 40 | -------------------------------------------------------------------------------- /docs/atomic-storage.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Shishir Mahajan 3 | % October 2015 4 | # NAME 5 | atomic-storage - Manage container storage. 6 | 7 | # SYNOPSIS 8 | **atomic storage COMMAND [OPTIONS]** 9 | 10 | atomic storage allows the user to easily manage container storage. 11 | You can reset your container environment back to its initial state as well 12 | as migrate all images, volumes, and containers from one version of atomic 13 | to another. With this command, users can quickly save all their data from 14 | the current atomic instance, change the container's content storage backend, 15 | and then import all their old data to the new system. 16 | 17 | # COMMANDS 18 | **export** 19 | 20 | export command will export all the current images, volumes, and containers 21 | to the specified directory (/var/lib/atomic/migrate by default), in the /images, 22 | /volumes, /containers subdirectories. 23 | 24 | **import** 25 | 26 | import command will import images, volumes, and containers from the specified 27 | directory (/var/lib/atomic/migrate by default) into the new atomic instance. 28 | 29 | **reset** 30 | Remove all containers/images from your system 31 | 32 | **modify** 33 | Modify the default storage setup 34 | 35 | # export OPTIONS 36 | **-h** **--help** 37 | Print usage statement 38 | 39 | **--graph** 40 | Root of the docker runtime. If you are running docker at the default 41 | location (/var/lib/docker), you don't need to pass this flag. However 42 | if you are running docker at a custom location. This flag must be set. 43 | 44 | **--dir** 45 | Directory in which to temporarily store the files (can be an existing 46 | directory, or the command will create one). If no directory is specified, 47 | /var/lib/atomic/migrate would be used as default. 48 | 49 | # Note: 50 | Atomic --assumeyes option can be used 51 | 52 | [**-y|--assumeyes**] 53 | Delete image(s) without conformation from the user 54 | 55 | # import OPTIONS 56 | **-h** **--help** 57 | Print usage statement 58 | 59 | **--graph** 60 | Root of the docker runtime. If you are running docker at the default 61 | location (/var/lib/docker), you don't need to pass this flag. However 62 | if you are running docker at a custom location. This flag must be set. 63 | 64 | **--dir** 65 | Directory from which to import the files (images, containers and volumes). 66 | If this flag is not set atomic storage will assume the import location to 67 | be /var/lib/atomic/migrate. Whether you set this flag or use the default, 68 | the directory must be present for the import to happen successfully. 69 | 70 | # modify OPTIONS 71 | **-h** **--help** 72 | Print usage statement 73 | 74 | **--add-device** 75 | Add block devices to storage pool. This command will expand your devicemapper 76 | storage pool by adding the block device. Only works with devicemapper driver. 77 | 78 | E.g atomic storage modify --add-device /dev/vdb will add /dev/vdb 79 | to storage pool. 80 | 81 | **--remove-device** 82 | Remove block devices from the storage pool. If a device is not empty, this 83 | command will try to first move its data to some other device in the pool. 84 | 85 | **--remove-unused-devices** 86 | Remove all block devices from the storage pool that are currently unused. 87 | 88 | **--driver** 89 | Backend storage driver for containers. This options the storage driver. 90 | Drivers supported: devicemapper, overlay, overlay2 91 | 92 | **--lvname** 93 | Logical volume name for container storage. 94 | E.g atomic storage modify --lvname="container-root-lv" 95 | --rootfs="/var/lib/containers" will create logical volume named 96 | container-root-lv and mount it on /var/lib/containers. 97 | Note: You must set --rootfs when setting --lvname. 98 | 99 | **--rootfs** 100 | Mountpath where logical volume for container storage would be mounted. 101 | E.g. atomic storage modify --rootfs="/var/lib/containers" 102 | --lvname="container-root-lv" will create logical volume named 103 | container-root-lv and mount it on /var/lib/containers. 104 | Note: You must set --lvname when setting --rootfs. 105 | 106 | **--lvsize** 107 | Logical volume size for container storage. It defaults to 40% of all free space. 108 | --lvsize can take values acceptable to "lvcreate -L" as well as some values 109 | acceptable to "lvcreate -l". If user intends to pass values acceptable to 110 | "lvcreate -l", then only those values which contains "%" in syntax are acceptable. 111 | If value does not contain "%" it is assumed value is suitable for "lvcreate -L". 112 | E.g. atomic storage modify --rootfs="/var/lib/containers" --lvname="container-root-lv" 113 | --lvsize=20%FREE will create logical volume named container-root-lv of size 114 | (20% of the available free space in the volume group) 115 | and mount it on /var/lib/containers. 116 | Note: You must set --lvname and --rootfs when setting --lvsize. 117 | 118 | **--vgroup** 119 | The name of the volume group for the storage pool. 120 | 121 | # reset OPTIONS 122 | **-h** **--help** 123 | Print usage statement 124 | 125 | **--graph** 126 | Root of the container runtime. atomic will search for either /var/lib/docker or 127 | /var/lib/docker-latest, if only one exists, atomic will select it as the default. 128 | If both exists or you are running docker with a graph storage at a non default 129 | location, you need to pass this flag. 130 | 131 | # HISTORY 132 | October 2015, Originally compiled by Shishir Mahajan (shishir dot mahajan at redhat dot com) 133 | -------------------------------------------------------------------------------- /docs/atomic-top.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Brent Baude 3 | % December 2015 4 | # NAME 5 | atomic-top - Run a top-like list of active container processes 6 | # SYNOPSIS 7 | **atomic top** 8 | [**-h**|**--help**] 9 | [**-d**][**-o, --optional=[time, stime, ppid, uid, gid, user, group]**][**-n**] 10 | [Containers to monitor] 11 | 12 | # DESCRIPTION 13 | 14 | **Atomic top** displays an interactive, top-like view of the processes running in active containers. 15 | 16 | While in the interactive view, you can sort the columns of information by pressing a single character 17 | that correlates to the column header. Any column that you can sort on will have a set of parentheses that surround 18 | a single character. For example, if you want to sort by the '(P)ID' column, 19 | simply press the 'p' key. 20 | 21 | Like top, you can exit the interactive view and return to the command line, use the 'q' character key. 22 | 23 | # OPTIONS 24 | **-h** **--help** 25 | Print usage statement 26 | 27 | **-d** 28 | Define the interval in seconds on which you want to refresh the process information. The interval should be an 29 | integer greater than 0. The default interval is set to 1. 30 | 31 | **-n** 32 | The number of iterations. Must be greater than 0. 33 | 34 | **-o** **--optional** 35 | Add more fields of data to collect for each process. The fields resemble fields commonly used by 36 | ps -o. They currently are: [time, stime, ppid, uid, gid, user, group] 37 | 38 | Specify one option per -o flag to include the fields. 39 | 40 | # EXAMPLES 41 | Monitor processes with default fields. 42 | 43 | atomic top 44 | 45 | Monitor processes with default fields on a 5 second interval for 3 iterations 46 | 47 | atomic top -d 5 -n 3 48 | 49 | Monitor processes and add in the data for the parent PIDs and UID. 50 | 51 | atomic top -o ppid -o uid 52 | 53 | # HISTORY 54 | December 2015, Originally written by Brent Baude (bbaude at redhat dot com) 55 | -------------------------------------------------------------------------------- /docs/atomic-uninstall.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Dan Walsh 3 | % January 2015 4 | # NAME 5 | atomic-uninstall - Remove/Uninstall container/container image from system 6 | 7 | # SYNOPSIS 8 | **atomic uninstall** 9 | [**--display**] 10 | [**-f**][**--force**] 11 | [**-h**|**--help**] 12 | [**-n**][**--name**[=*NAME*]] 13 | [**--storage**] 14 | IMAGE [ARG...] 15 | 16 | # DESCRIPTION 17 | **atomic uninstall** attempts to read the `LABEL UNINSTALL` field in the 18 | container IMAGE, if this field does not exist **atomic uninstall** will just 19 | uninstall the image. 20 | 21 | The image won't be removed if there are containers using it and `--force` is not used. 22 | 23 | If the container image has a LABEL UNINSTALL instruction like the following: 24 | 25 | `LABEL UNINSTALL /usr/bin/docker run -t -i --rm \${OPT1} --privileged -v /:/host --net=host --ipc=host --pid=host -e HOST=/host -e NAME=${NAME} -e IMAGE=${IMAGE} -e CONFDIR=\/etc/${NAME} -e LOGDIR=/var/log/\${NAME} -e DATADIR=/var/lib/\${NAME} ${IMAGE} \${OPT2} /bin/uninstall.sh \${OPT3}` 26 | 27 | `atomic uninstall` will set the following environment variables for use in the command: 28 | 29 | **NAME** 30 | The name specified via the command. NAME will be replaced with IMAGE if it is not specified. 31 | 32 | **IMAGE** 33 | The name and image specified via the command. 34 | 35 | **OPT1, OPT2, OPT3** 36 | Additional options which can be specified via the command. 37 | 38 | **SUDO_UID** 39 | The `SUDO_UID` environment variable. This is useful with the docker `-u` option for user space tools. If the environment variable is not available, the value of `/proc/self/loginuid` is used. 40 | 41 | **SUDO_GID** 42 | The `SUDO_GID` environment variable. This is useful with the docker `-u` option for user space tools. If the environment variable is not available, the default GID of the value for `SUDO_UID` is used. If this value is not available, the value of `/proc/self/loginuid` is used. 43 | 44 | Any additional arguments will be appended to the command. 45 | 46 | # OPTIONS: 47 | **--display** 48 | Display the image's uninstall options and environment variables 49 | populated into the uninstall command. 50 | The uninstall command will not execute if --display is specified. 51 | If --display is not specified the uninstall command will execute. 52 | 53 | **-f** **--force** 54 | Remove all containers based on this image before removing the image. 55 | 56 | **-h** **--help** 57 | Print usage statement 58 | 59 | **-n** **--name**="" 60 | If name is specified `atomic uninstall` will uninstall the named container from the system, otherwise it will uninstall the container images. 61 | 62 | **--storage** 63 | The --storage option will direct atomic where it should look for the image 64 | prior to uninstalling. Valid options are `docker` and `ostree`. 65 | 66 | # HISTORY 67 | January 2015, Originally compiled by Daniel Walsh (dwalsh at redhat dot com) 68 | -------------------------------------------------------------------------------- /docs/atomic-unmount.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) 2 | % Will Temple 3 | % June 2015 4 | # NAME 5 | atomic-unmount - Unmount Images/Containers 6 | 7 | # SYNOPSIS 8 | **atomic unmount** 9 | [**-h**|**--help**] 10 | DIRECTORY 11 | 12 | # DESCRIPTION 13 | **atomic unmount** will unmount a container/image previously mounted with 14 | **atomic mount**. If the UID of the user is not zero, i.e. if the user 15 | is not root, it will expect the image being deleted was mounted by 16 | non-root user and will delete the files rather than use the unmount 17 | system call. 18 | 19 | # OPTIONS: 20 | **-h** **--help** 21 | Print usage statement 22 | 23 | # HISTORY 24 | June 2015, Originally compiled by William Temple (wtemple at redhat dot com) 25 | -------------------------------------------------------------------------------- /docs/atomic.1.md: -------------------------------------------------------------------------------- 1 | % ATOMIC(1) Atomic Man Pages 2 | % Dan Walsh 3 | % January 2015 4 | # NAME 5 | atomic \- Atomic Management Tool 6 | 7 | # SYNOPSIS 8 | **atomic** [OPTIONS] COMMAND [arg...] 9 | {containers,diff,images,install,mount,pull,push,run,scan,sign,stop,storage,migrate,top,trust,uninstall,unmount,umount,update,verify,version} 10 | 11 | [**-h**|**--help**] 12 | 13 | # DESCRIPTION 14 | Atomic Management Tool 15 | 16 | # OPTIONS 17 | **-h** **--help** 18 | Print usage statement 19 | 20 | **-v** **--version** 21 | Show atomic version 22 | 23 | **--debug** 24 | Show debug messages 25 | 26 | **-y** **--assumeyes** 27 | automatically answer yes for all questions 28 | 29 | # ENVIRONMENT VARIABLES 30 | 31 | **ATOMIC_CONF** The location of the atomic configuration file (normally /etc/atomic.conf) can be 32 | overridden with the _ATOMIC_CONF_ environment variable 33 | 34 | **ATOMIC_CONFD** The location of the atomic configuration directory (normally /etc/atomic.d/) can be 35 | overridden with the _ATOMIC_CONFD_ environment variable. 36 | 37 | # COMMANDS 38 | **atomic-containers(1)** 39 | operations on installed containers 40 | 41 | **atomic-diff(1)** 42 | show the differences between two images|containers' RPMs 43 | 44 | **atomic-host(1)** 45 | execute commands to manage an Atomic host. 46 | 47 | Note: only available on atomic host platforms. 48 | 49 | **atomic-images(1)** 50 | operations on container images 51 | 52 | **atomic-install(1)** 53 | execute commands on installed images 54 | 55 | **atomic-mount(1)** 56 | mount image or container to filesystem 57 | 58 | **atomic-pull(1)** 59 | pull latest image from repository 60 | 61 | **atomic-push(1)** 62 | push container image to a repository 63 | 64 | **atomic-run(1)** 65 | execute image run method (default) 66 | 67 | **atomic-scan(1)** 68 | scan an image or container for CVEs 69 | 70 | **atomic-sign(1)** 71 | sign an image 72 | 73 | **atomic-stop(1)** 74 | execute container image stop method 75 | 76 | **atomic-storage(1)** 77 | manage the container storage on the system 78 | 79 | **atomic-top(1)** 80 | display a top-like list of container processes 81 | 82 | **atomic-trust(1)** 83 | manage system container trust policy 84 | 85 | **atomic-uninstall(1)** 86 | uninstall container from system 87 | 88 | **atomic-unmount(1)** 89 | unmount previously mounted image or container 90 | 91 | **atomic-update(1)** 92 | Downloads the latest container image. 93 | 94 | # CONNECTING TO DOCKER ENGINE 95 | 96 | By default, `atomic` command connects to docker engine via UNIX domain socket 97 | located at `/var/run/docker.sock`. You can use different connection method via 98 | setting several environment variables: 99 | 100 | **DOCKER_HOST** — this variable specifies connection string. If your engine 101 | listens on UNIX domain socket, you can specify the path via 102 | `http+unix://`, e.g. `http+unix://var/run/docker2.sock`. For TCP the 103 | string has this form: `tcp://:`, e.g. `tcp://127.0.0.1:2375` 104 | 105 | **DOCKER_TLS_VERIFY** — enables TLS verification if it contains any value, 106 | otherwise it disables the verification 107 | 108 | **DOCKER_CERT_PATH** — path to directory with TLS certificates, files in the 109 | directory need to have specific names: 110 | 111 | **cert.pem** — client certificate 112 | 113 | **key.pem** — client key 114 | 115 | **ca.pem** — CA certificate 116 | 117 | For more info, please visit upstream docs: 118 | 119 | **https://docs.docker.com/engine/security/https/** 120 | **https://docs.docker.com/machine/reference/env/** 121 | 122 | 123 | # HISTORY 124 | January 2015, Originally compiled by Daniel Walsh (dwalsh at redhat dot com) 125 | November, 2015 Addition of scan and diff by Brent Baude (bbaude at dot com) 126 | -------------------------------------------------------------------------------- /docs/install/Debian.md: -------------------------------------------------------------------------------- 1 | #INSTALL 2 | `atomic` can be installed through below methods 3 | 4 | ##Make 5 | On Debian, You will need to install the required build dependencies to build `atomic`. 6 | 7 | [Docker](https://docs.docker.com/engine/installation/linux/docker-ce/debian/) and [Golang](https://golang.org) are required to build `atomic`. 8 | 9 | "rpm" is required in order to `diff` two Docker images. 10 | 11 | ``` 12 | apt-get install go-md2man rpm python-selinux python-rpm python-dbus python-slip python-slip-dbus python-gobject python-yaml python-dateutil 13 | ``` 14 | 15 | Get the code 16 | ``` 17 | git clone https://github.com/projectatomic/atomic 18 | cd atomic 19 | ``` 20 | 21 | Build and install it. 22 | ``` 23 | pip install -r requirements.txt 24 | make install 25 | ``` 26 | 27 | Your install will now be complete! 28 | 29 | ``` 30 | ▶ atomic --version 31 | 1.8 32 | ``` 33 | 34 | ##Notes 35 | 36 | Warning: Atomic no longer packages the CLI as an egg and thus upgrading from `atomic` 1.5 to 1.8 requires removing conflicting folders. 37 | 38 | ``` 39 | rm -rf /usr/lib/python2.7/site-packages/Atomic/ /usr/lib/python2.7/site-packages/atomic-* 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/install/Fedora.md: -------------------------------------------------------------------------------- 1 | #INSTALL 2 | atomic can be installed through below methods 3 | 4 | ##Yum / DNF 5 | 6 | The Atomic RPM is packaged within Fedora 21 or later. 7 | 8 | ``` 9 | yum install atomic 10 | # or 11 | dnf install atomic 12 | ``` 13 | 14 | ##Make 15 | On Fedora, You need to install required build dependencies 16 | ``` 17 | yum-builddep atomic 18 | yum install -y python-requests libselinux-python python3-docker \ 19 | python-dateutil python-yaml pylint python-slip-dbus python-gobject 20 | # or 21 | dnf builddep atomic 22 | dnf install -y python-requests libselinux-python python3-docker \ 23 | python-dateutil python-yaml pylint python-slip-dbus python-gobject 24 | ``` 25 | 26 | Optionally, to use the builddep plugin in DNF you need to install dnf-plugins-core 27 | ``` 28 | dnf install dnf-plugins-core 29 | ``` 30 | 31 | Get the code 32 | ``` 33 | git clone https://github.com/projectatomic/atomic 34 | cd atomic 35 | ``` 36 | 37 | Build and install 38 | ``` 39 | make all 40 | make install 41 | ``` 42 | 43 | Your install will now be complete! 44 | 45 | ``` 46 | ▶ atomic --version 47 | 1.8 48 | ``` 49 | 50 | ##Test 51 | 52 | To test the checked out tree, install dependencies 53 | ``` 54 | dnf install -y python3-pylint /usr/bin/coverage2 55 | ``` 56 | 57 | Start the docker daemon 58 | ``` 59 | systemctl start docker 60 | ``` 61 | 62 | Run the tests 63 | ``` 64 | make test 65 | ``` 66 | 67 | ##Notes 68 | 69 | Warning: Atomic no longer packages the CLI as an egg and thus upgrading from `atomic` 1.5 to 1.8 requires removing conflicting folders. 70 | 71 | ``` 72 | rm -rf /usr/lib/python2.7/site-packages/Atomic/ /usr/lib/python2.7/site-packages/atomic-* 73 | ``` 74 | -------------------------------------------------------------------------------- /gotar.go: -------------------------------------------------------------------------------- 1 | // This source code will generate gotar binary 2 | // which is needed for atomic migrate export and import commands. 3 | 4 | package main 5 | 6 | import ( 7 | "archive/tar" 8 | "io" 9 | "log" 10 | "os" 11 | "path" 12 | "path/filepath" 13 | ) 14 | 15 | func main() { 16 | 17 | if len(os.Args) < 3 { 18 | log.Fatalln("Missing arguments") 19 | } 20 | 21 | if os.Args[1] == "" { 22 | log.Fatalln("tar flag cannot be empty. Please use -cf for creation or -xf for extraction") 23 | } 24 | 25 | if os.Args[1] != "-cf" && os.Args[1] != "-xf" { 26 | log.Fatalf("%s is not a valid tar flag\n", os.Args[1]) 27 | } 28 | 29 | if os.Args[2] == "" { 30 | log.Fatalln("Destination filename cannot be empty") 31 | } 32 | 33 | if os.Args[1] == "-cf" && os.Args[3] == "" { 34 | log.Fatalln("Source directory cannot be empty") 35 | } 36 | 37 | tarFlag := os.Args[1] 38 | destinationFilename := os.Args[2] 39 | 40 | if tarFlag == "-cf" { 41 | sourceDir := os.Args[3] 42 | tarDir(destinationFilename, sourceDir) 43 | } else if tarFlag == "-xf" { 44 | untarDir(destinationFilename) 45 | } 46 | } 47 | 48 | func checkError(err error) { 49 | if err != nil { 50 | log.Fatalln(err) 51 | } 52 | } 53 | 54 | func tarDir(destinationFilename, sourceDir string) { 55 | 56 | if destinationFilename[len(destinationFilename)-3:] != "tar" { 57 | log.Fatalln("Please provide a valid tar filename") 58 | } 59 | 60 | tarFile, err := os.Create(destinationFilename) 61 | checkError(err) 62 | 63 | defer tarFile.Close() 64 | 65 | var fileWriter io.WriteCloser = tarFile 66 | tarfileWriter := tar.NewWriter(fileWriter) 67 | defer tarfileWriter.Close() 68 | 69 | walkTar(sourceDir, tarfileWriter) 70 | } 71 | 72 | func walkTar(dirPath string, tarfileWriter *tar.Writer) { 73 | 74 | dir, err := os.Open(dirPath) 75 | checkError(err) 76 | 77 | dirInfo, err := dir.Stat() 78 | checkError(err) 79 | 80 | // prepare the tar header for dir entry. 81 | dheader, err := tar.FileInfoHeader(dirInfo, "") 82 | checkError(err) 83 | 84 | dheader.Name = dir.Name()[1:] 85 | 86 | err = tarfileWriter.WriteHeader(dheader) 87 | checkError(err) 88 | 89 | files, err := dir.Readdir(0) // grab the files list 90 | checkError(err) 91 | 92 | for _, fileInfo := range files { 93 | if fileInfo.IsDir() { 94 | walkTar(path.Join(dir.Name(), fileInfo.Name()), tarfileWriter) 95 | 96 | } else { 97 | file, err := os.Open(dir.Name() + string(filepath.Separator) + fileInfo.Name()) 98 | checkError(err) 99 | 100 | defer file.Close() 101 | 102 | // prepare the tar header for file entry. 103 | 104 | header, err := tar.FileInfoHeader(fileInfo, "") 105 | checkError(err) 106 | 107 | header.Name = file.Name()[1:] 108 | 109 | err = tarfileWriter.WriteHeader(header) 110 | checkError(err) 111 | 112 | _, err = io.Copy(tarfileWriter, file) 113 | checkError(err) 114 | } 115 | } 116 | } 117 | 118 | func untarDir(destinationFilename string) { 119 | 120 | var fileReader io.ReadCloser 121 | 122 | if destinationFilename == "-" { 123 | fileReader = os.Stdin 124 | } else { 125 | if destinationFilename[len(destinationFilename)-3:] != "tar" { 126 | log.Fatalln("Please provide a valid tar filename") 127 | } 128 | 129 | file, err := os.Open(destinationFilename) 130 | checkError(err) 131 | defer file.Close() 132 | 133 | fileReader = file 134 | } 135 | 136 | tarfileReader := tar.NewReader(fileReader) 137 | pwd, err := os.Getwd() 138 | checkError(err) 139 | 140 | for { 141 | header, err := tarfileReader.Next() 142 | if err != nil { 143 | if err == io.EOF { 144 | break 145 | } 146 | log.Fatalln(err) 147 | } 148 | 149 | fileInfo := header.FileInfo() 150 | 151 | if fileInfo.IsDir() { 152 | err = os.MkdirAll(path.Join(pwd, header.Name), fileInfo.Mode()) 153 | checkError(err) 154 | } else { 155 | tarFile, err := os.Create(path.Join(pwd, header.Name)) 156 | checkError(err) 157 | defer tarFile.Close() 158 | 159 | _, err = io.Copy(tarFile, tarfileReader) 160 | checkError(err) 161 | } 162 | 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /org.atomic.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /org.atomic.policy: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Red Hat Inc. 8 | http://www.redhat.com 9 | 10 | 11 | Atomic write access 12 | System policy prevents read/write access to Atomic 13 | 14 | auth_admin 15 | no 16 | auth_admin_keep 17 | 18 | 19 | 20 | Atomic Read access 21 | System policy prevents read access to Atomic 22 | 23 | auth_admin 24 | no 25 | yes 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /org.atomic.service: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=org.atomic 3 | Exec=/usr/share/atomic/atomic_dbus.py 4 | User=root 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.4.3 2 | setuptools 3 | docker 4 | websocket-client>=0.11.0 5 | six>=1.3.0 6 | xattr 7 | python-dateutil 8 | PyYAML 9 | urllib3 10 | pylint 11 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Author: Dan Walsh 4 | from distutils.core import setup 5 | import Atomic as _Atomic 6 | 7 | with open('requirements.txt') as f: 8 | requirements = f.read().splitlines() 9 | 10 | setup( 11 | name="atomic", scripts=["atomic", "atomic_dbus.py"], 12 | version=_Atomic.__version__, 13 | author=_Atomic.__author__, 14 | author_email=_Atomic.__author_email__, 15 | packages=["Atomic", "Atomic/backends", "Atomic/objects"], 16 | data_files=[('/etc/dbus-1/system.d/', ["org.atomic.conf"]), 17 | ('/usr/share/dbus-1/system-services', ["org.atomic.service"]), 18 | ('/usr/share/polkit-1/actions/', ["org.atomic.policy"]), 19 | ("/usr/share/bash-completion/completions/", 20 | ["bash/atomic"])] 21 | ) 22 | -------------------------------------------------------------------------------- /tests/atomicClient.html: -------------------------------------------------------------------------------- 1 | 2 | Atomic Client 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/integration/_test_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | if test -e /run/ostree-booted; then 5 | exit 77 6 | fi 7 | 8 | # uncomment to test locally 9 | # WORK_DIR=./test-run/ 10 | # mkdir -p $WORK_DIR 11 | # ATOMIC="python2 ./atomic --debug" 12 | # DOCKER="/usr/bin/docker" 13 | export ATOMIC_INSTALL_JSON=${WORK_DIR}/install.json 14 | 15 | # we want this image to be present in environment after this integration test is run 16 | PERSISTENT_IMAGE="atomic-test-system-hostfs" 17 | IMAGE="atomic-install-test-image" 18 | CONTAINER_NAME="atomic-test-container" 19 | NON_EXISTENT_IMAGE="non-existent-image" 20 | 21 | # get rid of all RPMs lingering from other tests 22 | LINGERING_SYSTEM_RPMS=$(rpm -qa | egrep "^atomic-container" || true) 23 | if [ -n "${LINGERING_SYSTEM_RPMS}" ] ; then dnf remove -y "${LINGERING_SYSTEM_RPMS}"; fi 24 | 25 | # test for correct error message if image doesn't exist 26 | ${ATOMIC} install --storage=docker ${NON_EXISTENT_IMAGE} 2>&1 | grep "RegistryInspectError: Unable to find ${NON_EXISTENT_IMAGE}" 27 | 28 | # ensure ${PERSISTENT_IMAGE} survives `rmi` 29 | ${DOCKER} tag ${PERSISTENT_IMAGE} ${IMAGE} 30 | 31 | teardown () { 32 | ${ATOMIC} -i uninstall --storage=docker --name=${CONTAINER_NAME} ${IMAGE} 33 | Failure here too, not well understood. Will follow up 34 | rpm -qa | grep ${CONTAINER_NAME} && { echo "package is installed when it should have been removed"; exit 1; } 35 | # ensure the $PERSISTENT_IMAGE is present 36 | ${DOCKER} inspect ${PERSISTENT_IMAGE} >/dev/null 37 | rm $(dirname $ATOMIC_INSTALL_JSON)/install.json.lock || true 38 | rm $(dirname $ATOMIC_INSTALL_JSON)/install.json || true 39 | } 40 | trap teardown EXIT 41 | 42 | ${ATOMIC} install --storage=docker --name=${CONTAINER_NAME} ${IMAGE} 43 | 44 | RPM_NAME=$(rpm -qa | egrep "^atomic-container-${CONTAINER_NAME}") 45 | 46 | FILE_LIST=$(rpm -ql $RPM_NAME) 47 | 48 | egrep "^/usr/local/lib/secret-message$" <<< "${FILE_LIST}" 49 | 50 | grep "\$RECEIVER" /usr/local/lib/secret-message 51 | 52 | docker inspect --format='{{.Config.Labels}}' ${IMAGE} | grep "atomic.has_install_files" 53 | 54 | # ensure that install.json file is valid json 55 | #INSTALL_JSON_CONTENT=$($PYTHON -m json.tool $ATOMIC_INSTALL_JSON) 56 | 57 | grep "\"container_name\": \"${CONTAINER_NAME}\"" <<< "$INSTALL_JSON_CONTENT" 58 | grep "\"system_package_nvra\": \"atomic-container-${CONTAINER_NAME}" <<< "$INSTALL_JSON_CONTENT" 59 | grep '"/usr/local/lib/placeholder-file"' <<< "$INSTALL_JSON_CONTENT" 60 | grep '"/usr/local/lib/secret-message"' <<< "$INSTALL_JSON_CONTENT" 61 | grep '"/usr/local/lib/secret-message-template"' <<< "$INSTALL_JSON_CONTENT" 62 | 63 | # test for correct error message when image is already installed 64 | ${ATOMIC} install --storage=docker --name=${CONTAINER_NAME} ${IMAGE} 2>&1 | grep "Image ${IMAGE} is already installed." 65 | -------------------------------------------------------------------------------- /tests/integration/setup-scripts/system_containers_setup.sh: -------------------------------------------------------------------------------- 1 | assert_not_reached() { 2 | echo $@ 1>&2 3 | exit 1 4 | } 5 | 6 | assert_not_matches() { 7 | if grep -q -e $@; then 8 | sed -e s',^,| ,' < $2 9 | assert_not_reached "Matched: " $@ 10 | fi 11 | } 12 | 13 | assert_matches() { 14 | if ! grep -q -e $@; then 15 | sed -e s',^,| ,' < $2 16 | assert_not_reached "Failed to match: " $@ 17 | fi 18 | } 19 | 20 | assert_equal() { 21 | if ! test $1 = $2; then 22 | assert_not_reached "Failed: not equal " $1 $2 23 | fi 24 | } 25 | 26 | 27 | # Skip the test if: 28 | # 1. OSTree or runc are not installed 29 | # 2. the version of runc is too low 30 | # 3. atomic has not --install --system 31 | # 4. skopeo copy to ostree fails 32 | 33 | ostree --version &>/dev/null || exit 77 34 | runc --version &>/dev/null || exit 77 35 | 36 | if runc --version | grep -q "version 0"; then 37 | exit 77 38 | fi 39 | 40 | 41 | ${ATOMIC} install --help 2>&1 > help.out 42 | grep -q -- --system help.out || exit 77 43 | 44 | export PYTHON=${PYTHON:-/usr/bin/python} 45 | export ATOMIC_OSTREE_REPO=${WORK_DIR}/repo 46 | export ATOMIC_OSTREE_CHECKOUT_PATH=${WORK_DIR}/checkout 47 | export NAME="test-system-container-$$" 48 | 49 | ostree --repo=$ATOMIC_OSTREE_REPO init 50 | 51 | if test -n "$SKOPEO_NO_OSTREE"; then 52 | exit 77 53 | fi 54 | 55 | # This is to prevent the case where the ostree checkout path 56 | # can be non-existent when no container installation happens prior 57 | mkdir -p $ATOMIC_OSTREE_CHECKOUT_PATH 58 | -------------------------------------------------------------------------------- /tests/integration/test_containers_list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # Test scripts run with PWD=tests/.. 6 | 7 | # The test harness exports some variables into the environment during 8 | # testing: PYTHONPATH (python module import path 9 | # WORK_DIR (a directory that is safe to modify) 10 | # DOCKER (the docker executable location) 11 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 12 | # SECRET (a generated sha256 hash inserted into test containers) 13 | 14 | # In addition, the test harness creates some images for use in testing. 15 | # See tests/test-images/ 16 | ATOMIC=$(grep -v -- --debug <<< "$ATOMIC") 17 | 18 | OUTPUT=$(/bin/true) 19 | 20 | ${ATOMIC} containers list --all -q -f runtime=docker | sort > atomic.ps.out 21 | docker ps --all -q | sort > docker.ps.out 22 | diff docker.ps.out atomic.ps.out 23 | 24 | ${ATOMIC} containers list -q -f runtime=Docker | sort > atomic.ps.out 25 | docker ps -q | sort > docker.ps.out 26 | diff docker.ps.out atomic.ps.out 27 | 28 | # Ensure that when json is requested and no containers are returned we still 29 | # get valid json ([]) 30 | ${ATOMIC} containers list --json -f container=idonotexist | grep "\[\]" 31 | -------------------------------------------------------------------------------- /tests/integration/test_diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | GOMTREE='/usr/bin/gomtree' 5 | 6 | # Test scripts run with PWD=tests/.. 7 | 8 | # The test harness exports some variables into the environment during 9 | # testing: PYTHONPATH (python module import path 10 | # WORK_DIR (a directory that is safe to modify) 11 | # DOCKER (the docker executable location) 12 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 13 | # SECRET (a generated sha256 hash inserted into test containers) 14 | 15 | # In addition, the test harness creates some images for use in testing. 16 | # See tests/test-images/ 17 | 18 | setup () { 19 | # Perform setup routines here. 20 | IMAGE="atomic-test-1" 21 | id1=`${DOCKER} create ${IMAGE} /bin/true` 22 | id2=`${DOCKER} create ${IMAGE} rpm -e vim-minimal` 23 | ${DOCKER} start ${id2} 24 | 25 | } 26 | 27 | teardown () { 28 | # Cleanup your test data. 29 | set +e 30 | ${DOCKER} rm ${id1} 31 | ${DOCKER} rm ${id2} 32 | set -e 33 | } 34 | # Utilize exit traps for cleanup wherever possible. Additional cleanup 35 | # logic can be added to a "cleanup stack", by cascading function calls 36 | # within traps. See tests/integration/test_mount.sh for an example. 37 | trap teardown EXIT 38 | if [ ! -e ${GOMTREE} ]; then 39 | exit 77 40 | fi 41 | 42 | setup 43 | 44 | OUTPUT=$(/bin/true) 45 | 46 | # Test atomic diff for files and RPMs 47 | ${ATOMIC} diff -r -v ${id1} ${id2} 1>/dev/null 48 | 49 | # Test atomic diff with RPMs and output to json 50 | ${ATOMIC} diff -r --json ${id1} ${id2} 1>/dev/null 51 | -------------------------------------------------------------------------------- /tests/integration/test_display.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # 6 | # 'atomic run --display' and 'atomic install --display' integration tests 7 | # AUTHOR: Sally O'Malley 8 | # 9 | ATOMIC=${ATOMIC:="/usr/bin/atomic"} 10 | ATOMIC=$(grep -v -- --debug <<< "$ATOMIC") 11 | DOCKER=${DOCKER:="/usr/bin/docker"} 12 | TNAME="test_display" 13 | 14 | teardown () { 15 | ${DOCKER} rm TEST3 TEST4 2> /dev/null || return 0 16 | } 17 | trap teardown EXIT 18 | 19 | # Remove the --user UID:GID 20 | OUTPUT=`${ATOMIC} run --display -n TEST1 atomic-test-1 | sed 's/ --user [0-9]*:[0-9]* / /' | xargs` 21 | OUTPUT2="/usr/bin/docker run -t -v /var/log/TEST1:/var/log -v /var/lib/TEST1:/var/lib --name TEST1 atomic-test-1 echo I am the run label." 22 | if [[ ${OUTPUT} != ${OUTPUT2} ]]; then 23 | echo "Failed ${TNAME} 1" 24 | exit 1 25 | fi 26 | 27 | OUTPUT=`${ATOMIC} install --display -n TEST2 atomic-test-1 | xargs` 28 | OUTPUT2="/usr/bin/docker run -v /etc/TEST2:/etc -v /var/log/TEST2:/var/log -v /var/lib/TEST2:/var/lib --name TEST2 atomic-test-1 echo I am the install label." 29 | if [[ ${OUTPUT} != ${OUTPUT2} ]]; then 30 | echo "Failed ${TNAME} 2" 31 | exit 1 32 | fi 33 | 34 | ${ATOMIC} install -n TEST3 atomic-test-1 35 | OUTPUT=`${DOCKER} logs TEST3 | tr -d '\r'` 36 | if [[ ${OUTPUT} != "I am the install label." ]]; then 37 | echo "Failed ${TNAME} 3" 38 | exit 1 39 | fi 40 | 41 | ${ATOMIC} run -n TEST4 atomic-test-1 42 | OUTPUT=`${DOCKER} logs TEST4 | tr -d '\r'` 43 | if [[ ${OUTPUT} != "I am the run label." ]]; then 44 | echo "Failed ${TNAME} 4" 45 | exit 1 46 | fi 47 | 48 | # The centos image does not have an INSTALL label, so `atomic install` should be 49 | # a noop. 50 | OUTPUT=`${ATOMIC} install --display -n TEST5 centos | xargs` 51 | if [[ -n ${OUTPUT} ]]; then 52 | echo "Failed ${TNAME} 5" 53 | exit 1 54 | fi 55 | 56 | # The centos image does not have an run label, so output should be $OUTPUT2 57 | OUTPUT=`${ATOMIC} run --display -n TEST6 centos /bin/ls | sed 's/ -t / /g' | xargs` 58 | OUTPUT2="docker run -i --name TEST6 centos /bin/ls" 59 | if [[ ${OUTPUT} != ${OUTPUT2} ]]; then 60 | echo "Failed ${TNAME} 6" 61 | exit 1 62 | fi 63 | 64 | # The centos image does not have an run label, so output should be $OUTPUT2 65 | OUTPUT=`${ATOMIC} run --display --spc -n TEST7 centos /bin/ls | sed 's/ -t / /g' | xargs` 66 | OUTPUT2="docker run -i --privileged -v /:/host -v /run:/run -v /etc/localtime:/etc/localtime -v /sys/fs/selinux:/sys/fs/selinux:ro --net=host --ipc=host --pid=host -e HOST=/host -e NAME=TEST7 -e IMAGE=centos -e SYSTEMD_IGNORE_CHROOT=1 --name TEST7 centos /bin/ls" 67 | 68 | if [[ ${OUTPUT} != ${OUTPUT2} ]]; then 69 | echo "Failed ${TNAME} 7" 70 | exit 1 71 | fi 72 | 73 | # Test for regression on atomic stop 74 | CID=`docker run -d atomic-test-6 sleep 100` 75 | OUTPUT=`${ATOMIC} stop --display ${CID}` 76 | OUTPUT2="docker stop ${CID}" 77 | docker stop ${CID} 78 | 79 | if [[ ${OUTPUT} != ${OUTPUT2} ]]; then 80 | echo "Failed ${TNAME} 8" 81 | exit 1 82 | fi 83 | 84 | for i in `docker ps --all --filter ancestor=atomic-test-1 --format "{{.ID}}"`; do 85 | docker rm -f $i 86 | done 87 | 88 | OUTPUT=`${ATOMIC} uninstall --display atomic-test-1` 89 | RESULT="/usr/bin/docker run -v /etc/atomic-test-1:/etc -v /var/log/atomic-test-1:/var/log -v /var/lib/atomic-test-1:/var/lib --name atomic-test-1 atomic-test-1 echo I am the uninstall label." 90 | if [[ ${OUTPUT} != ${RESULT} ]]; then 91 | echo "Uninstall display failed for uninstall-1" 92 | exit 1 93 | fi 94 | 95 | # Add test case for issue #1217 96 | export ATOMIC_INSTALL_JSON=empty.json 97 | ${ATOMIC} install atomic-test-1 98 | ${ATOMIC} run atomic-test-1 99 | ${ATOMIC} stop atomic-test-1 100 | ${ATOMIC} --assumeyes containers delete atomic-test-1 101 | ${ATOMIC} --debug uninstall atomic-test-1 102 | -------------------------------------------------------------------------------- /tests/integration/test_help.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -eu 3 | # IFS=$'\n\t' 4 | 5 | # ATOMIC="python2 ./atomic --debug" 6 | 7 | # Test scripts run with PWD=tests/.. 8 | 9 | # The test harness exports some variables into the environment during 10 | # testing: PYTHONPATH (python module import path 11 | # WORK_DIR (a directory that is safe to modify) 12 | # DOCKER (the docker executable location) 13 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 14 | # SECRET (a generated sha256 hash inserted into test containers) 15 | 16 | # In addition, the test harness creates some images for use in testing. 17 | # See tests/test-images/ 18 | 19 | OUTPUT=$(/bin/true) 20 | 21 | # some other test leaves an image inside ostree backend, so 22 | # let's clean the environment first 23 | ostree --repo=${ATOMIC_OSTREE_REPO} refs --delete ociimage || true 24 | 25 | # Test standard help in man format 26 | if [ -x /usr/bin/groff ]; then 27 | MOUNTS_NUM=$(mount | wc -l) 28 | mkdir -p /run/atomic 29 | TEMPFILES_NUM=$(ls -1 /run/atomic | wc -l) 30 | ${ATOMIC} help atomic-test-1 1>/dev/null 31 | MOUNTS_NUM_AFTER=$(mount | wc -l) 32 | TEMPFILES_AFTER_NUM=$(ls -1 /run/atomic | wc -l) 33 | # Make sure that container mount is unmounted 34 | if [[ ${MOUNTS_NUM} != ${MOUNTS_NUM_AFTER} ]]; then 35 | # Test failed 36 | echo "It looks like that container is not unmounted after showing help file." 37 | exit 1 38 | fi 39 | # Make sure no temp files linger in /tmp 40 | if [[ ${TEMPFILES_NUM} != ${TEMPFILES_AFTER_NUM} ]]; then 41 | # Test failed 42 | echo "Some temporary files from /run/atomic were not cleaned." 43 | exit 1 44 | fi 45 | fi 46 | 47 | # Test if the table got preprocessed 48 | if ${ATOMIC} help atomic-test-1 | grep "allbox;"; then false; fi 49 | 50 | # Test override label - uppercase help 51 | ${ATOMIC} help atomic-test-3 | grep "Testing help" 52 | 53 | # Test override label - lowercase help 54 | ${ATOMIC} help atomic-test-4 | grep "Testing help" 55 | 56 | set +e 57 | CENTOS_OUTPUT=$(${ATOMIC} help centos 2>&1) 58 | set -e 59 | grep "There is no help for centos" <<< "${CENTOS_OUTPUT}" 60 | 61 | # Ensure atomic returns >0 62 | rc=0 63 | ${ATOMIC} help centos >/dev/null || rc=$? 64 | if [[ ${rc} != 1 ]]; then 65 | # Test failed 66 | echo "This test should result in a return code of 1" 67 | exit 1 68 | fi 69 | -------------------------------------------------------------------------------- /tests/integration/test_images_list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # Test images listing and filtering functionality 6 | 7 | IMAGE="atomic-test-3" 8 | IMAGE_SECRET="atomic-test-secret" 9 | TAGGED_IMAGE="local/at3" 10 | RUNNING_CONTAINER="testContainerOut" 11 | 12 | assert_not_reached() { 13 | echo $@ 1>&2 14 | exit 1 15 | 16 | } 17 | 18 | assert_matches() { 19 | if ! grep -q -e $@; then 20 | sed -e s',^,| ,' < $2 21 | assert_not_reached "Failed to match: " $@ 22 | fi 23 | } 24 | 25 | assert_not_matches() { 26 | if grep -q -e $@; then 27 | sed -e s',^,| ,' < $2 28 | assert_not_reached "Matched: " $@ 29 | fi 30 | } 31 | 32 | setup () { 33 | ${DOCKER} tag ${IMAGE} ${TAGGED_IMAGE}:latest 34 | } 35 | 36 | teardown () { 37 | set +e 38 | ${DOCKER} rm ${RUNNING_CONTAINER} 39 | ${DOCKER} rmi ${TAGGED_IMAGE}:latest 40 | set -e 41 | } 42 | 43 | trap teardown exit 44 | 45 | setup 46 | 47 | ${ATOMIC} images list > ${WORK_DIR}/images.out 48 | assert_matches ${IMAGE} ${WORK_DIR}/images.out 49 | assert_matches ${IMAGE_SECRET} ${WORK_DIR}/images.out 50 | assert_matches ${TAGGED_IMAGE} ${WORK_DIR}/images.out 51 | 52 | # Testing --all 53 | ${ATOMIC} images list --all > ${WORK_DIR}/images.all.out 54 | test $(wc -l < ${WORK_DIR}/images.out) -lt $(wc -l < ${WORK_DIR}/images.all.out) 55 | assert_matches '' ${WORK_DIR}/images.all.out 56 | 57 | # Testing filters and used tag > 58 | ${ATOMIC} images list -f repo=${IMAGE} > ${WORK_DIR}/images.out 59 | assert_matches ${IMAGE} ${WORK_DIR}/images.out 60 | assert_not_matches "> "${IMAGE} ${WORK_DIR}/images.out 61 | ${DOCKER} run --name=${RUNNING_CONTAINER} ${IMAGE} 62 | ${ATOMIC} images list -f repo=${IMAGE} > ${WORK_DIR}/images.out 63 | assert_matches "> "${IMAGE} ${WORK_DIR}/images.out 64 | 65 | ${ATOMIC} images list -f type=docker > ${WORK_DIR}/images.out 66 | assert_matches ${IMAGE} ${WORK_DIR}/images.out 67 | ${ATOMIC} images list -f repo=non-existing-repo > ${WORK_DIR}/images.out 68 | assert_not_matches ${IMAGE} ${WORK_DIR}/images.out 69 | ${ATOMIC} images list -f repo=${IMAGE} -f type=docker > ${WORK_DIR}/images.out 70 | assert_matches ${IMAGE} ${WORK_DIR}/images.out 71 | assert_not_matches ${IMAGE_SECRET} ${WORK_DIR}/images.out 72 | 73 | OUTPUT=$(! ${ATOMIC} images list -f not-a-filter=${IMAGE} 2>&1) 74 | grep "not valid" <<< $OUTPUT 75 | 76 | # Testing noheading/no-trunc 77 | ${ATOMIC} images list --noheading > ${WORK_DIR}/images.out 78 | assert_not_matches 'REPOSITORY' ${WORK_DIR}/images.out 79 | 80 | IMAGE_ID=$(${DOCKER} images --no-trunc | grep ${IMAGE} | awk '{print $3}' | cut -d ":" -f 2) 81 | ${ATOMIC} images list --no-trunc > ${WORK_DIR}/images.out 82 | assert_matches ${IMAGE_ID} ${WORK_DIR}/images.out 83 | 84 | # Testing quiet/json 85 | ${ATOMIC} images list -q > ${WORK_DIR}/images.out 86 | assert_not_matches ${IMAGE} ${WORK_DIR}/images.out 87 | assert_matches ${IMAGE_ID:0:12} ${WORK_DIR}/images.out 88 | 89 | ${ATOMIC} images list --json > ${WORK_DIR}/images.out 90 | assert_matches ${IMAGE_ID} ${WORK_DIR}/images.out 91 | assert_matches ${IMAGE} ${WORK_DIR}/images.out 92 | 93 | # Test that filters work together 94 | ${ATOMIC} images list -a -q --no-trunc -f repo=${IMAGE} -f type=docker > ${WORK_DIR}/images.out 95 | assert_matches ${IMAGE_ID} ${WORK_DIR}/images.out 96 | 97 | # Check that the tagged image is being displayed 98 | ${ATOMIC} images list > ${WORK_DIR}/images.out 99 | assert_matches ${TAGGED_IMAGE} ${WORK_DIR}/images.out 100 | ${ATOMIC} images list --json > ${WORK_DIR}/images.out 101 | assert_matches ${TAGGED_IMAGE} ${WORK_DIR}/images.out 102 | ${ATOMIC} images list -f repo=${TAGGED_IMAGE} > ${WORK_DIR}/images.out 103 | assert_matches ${TAGGED_IMAGE} ${WORK_DIR}/images.out 104 | ${ATOMIC} images list -f repo=${TAGGED_IMAGE} -q --no-trunc> ${WORK_DIR}/images.out 105 | assert_matches ${IMAGE_ID} ${WORK_DIR}/images.out 106 | -------------------------------------------------------------------------------- /tests/integration/test_info.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # The debug information will cause test failures because 6 | # the force arg will be different. This messes up the 7 | # equality testing between a remote and local image. 8 | # Removing the --debug 9 | ATOMIC=$(grep -v -- --debug <<< "$ATOMIC") 10 | 11 | EXPECTED_T1="Checksum: $(sha256sum ./tests/test-images/Dockerfile.1)" 12 | 13 | validTest1 () { 14 | for e in ${TEST_1}; do 15 | [[ $e = ${EXPECTED_T1}* ]] && return 0; 16 | done 17 | return 1 18 | } 19 | 20 | 21 | TEST_1=`${ATOMIC} info atomic-test-1` 22 | TEST_CENTOS=`${ATOMIC} info centos:latest | sort` 23 | 24 | set +e 25 | 26 | TEST_CENTOS_REMOTE=`${ATOMIC} info --remote centos:latest | sort` 27 | HAS_REMOTE=$? 28 | TEST_DOES_NOT_EXIST=`${ATOMIC} info this-is-not-a-real-image` 29 | 30 | set -e 31 | 32 | echo $TEST_1 33 | 34 | if [[ "${HAS_REMOTE}" -eq 0 ]]; then 35 | if [[ ${TEST_CENTOS_REMOTE} != ${TEST_CENTOS} ]]; then 36 | exit 1 37 | fi 38 | fi 39 | 40 | # Disabled temporarily until skopeo discussion 41 | #if [[ "${TEST_DOES_NOT_EXIST}" != "" ]]; then 42 | # exit 1 43 | #fi 44 | 45 | validTest1 46 | 47 | if [[ $? -ne 0 ]]; then 48 | exit 1 49 | fi 50 | -------------------------------------------------------------------------------- /tests/integration/test_migrate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | #With the inclusion of this PR (https://github.com/projectatomic/atomic/pull/294) 6 | #atomic storage export/import will only work with docker 1.10 support. 7 | #Skip this test, until we move to docker 1.10. 8 | 9 | echo "WARNING: skipping test_migrate.sh since it is only supported with docker 1.10 onwards." 10 | exit 0 11 | 12 | # 13 | # 'atomic storage' integration tests (non-live) 14 | # AUTHOR: Shishir Mahajan 15 | # 16 | 17 | if [[ "$(id -u)" -ne "0" ]]; then 18 | echo "Atomic storage tests require root access. Please try again." 19 | exit 1 20 | fi 21 | 22 | init=$(ps -q 1 -o comm=) 23 | if [ "$init" != "systemd" ];then 24 | echo "Systemd init system is required to run atomic storage tests. Skipping these tests." 25 | exit 0 26 | fi 27 | 28 | if ! systemctl is-active docker >/dev/null; then 29 | echo "Docker daemon is not running" 30 | exit 1 31 | fi 32 | pid=$(systemctl show -p MainPID docker.service) 33 | dockerPid=$(echo ${pid#*=}) 34 | dockerCmdline=$(cat /proc/$dockerPid/cmdline) 35 | if [[ $dockerCmdline =~ "-g=" ]] || [[ $dockerCmdline =~ "-g/" ]] || [[ $dockerCmdline =~ "--graph" ]];then 36 | echo "Docker is not located at the default (/var/lib/docker) root location. Skipping these tests." 37 | exit 0 38 | fi 39 | 40 | if [ ! -f /etc/sysconfig/docker ];then 41 | echo "Atomic storage tests require /etc/sysconfig/docker to exist. Skipping these tests." 42 | exit 0 43 | fi 44 | 45 | if [ ! -f /etc/sysconfig/docker-storage ];then 46 | echo "Atomic storage tests require /etc/sysconfig/docker-storage to exist. Skipping these tests." 47 | exit 0 48 | fi 49 | 50 | setup () { 51 | CNT1=$(${DOCKER} create --name test-migrate-1 -v /tmp fedora /bin/bash) 52 | CNT2=$(${DOCKER} create --name test-migrate-2 -v /tmp busybox /bin/bash) 53 | } 54 | 55 | cleanup () { 56 | systemctl stop docker 57 | 58 | if findmnt /var/lib/docker >/dev/null; then 59 | umount /var/lib/docker 60 | fi 61 | 62 | rm -rf /var/lib/overlayfs 63 | 64 | if [ -f /etc/sysconfig/docker.backup ]; then 65 | mv /etc/sysconfig/docker{.backup,} 66 | fi 67 | 68 | if [ -f /etc/sysconfig/docker-storage.backup ]; then 69 | mv /etc/sysconfig/docker-storage{.backup,} 70 | fi 71 | 72 | systemctl start docker 73 | 74 | for cnt in test-migrate-1 test-migrate-2; do 75 | if ${DOCKER} inspect $cnt &>/dev/null; then 76 | ${DOCKER} rm -f -v $cnt 77 | fi 78 | done 79 | } 80 | 81 | trap cleanup EXIT 82 | 83 | # Test for atomic storage export and import using the default graph 84 | # at /var/lib/docker 85 | atomic_storage_migrate () { 86 | setup 87 | echo 'y'|${ATOMIC} storage export --dir "$(pwd)/migrate-dir" 88 | switch_docker_storage 89 | echo 'y'|${ATOMIC} storage import --dir "$(pwd)/migrate-dir" 90 | systemctl restart docker 91 | 92 | # check that the containers were actually migrated (this implicitly also 93 | # checks that at least the fedora and busybox images were also migrated) 94 | for cnt in $CNT1 $CNT2; do 95 | ${DOCKER} inspect $cnt 96 | done 97 | } 98 | 99 | switch_docker_storage () { 100 | systemctl stop docker 101 | mkdir -p /var/lib/overlayfs 102 | mount -o bind /var/lib/overlayfs /var/lib/docker 103 | restorecon -R -v /var/lib/docker 104 | 105 | # NB: Let's not actually switch over to overlayfs for now because it can 106 | # trigger a kernel mm bug from the overlay module's allocations. There is a 107 | # "fix" (an overlay patch to work around the mm bug) which has made it 108 | # upstream, but is not yet in the stable kernels. Until then, let's not use 109 | # overlayfs (see https://github.com/coreos/bugs/issues/489 for more info). 110 | 111 | #cp /etc/sysconfig/docker /etc/sysconfig/docker.backup 112 | #cp /etc/sysconfig/docker-storage /etc/sysconfig/docker-storage.backup 113 | #sed -i "/OPTIONS/c OPTIONS=''" /etc/sysconfig/docker 114 | #sed -i '/DOCKER_STORAGE_OPTIONS/c DOCKER_STORAGE_OPTIONS="-s overlay"' /etc/sysconfig/docker-storage 115 | 116 | systemctl start docker 117 | } 118 | 119 | atomic_storage_migrate 120 | -------------------------------------------------------------------------------- /tests/integration/test_mount.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # 6 | # 'atomic mount' integration tests 7 | # AUTHOR: William Temple 8 | # 9 | 10 | if [[ "$(id -u)" -ne "0" ]]; then 11 | echo "Atomic mount tests require root access to manipulate devices." 12 | exit 1 13 | fi 14 | 15 | setup () { 16 | MNT_WORK="${WORK_DIR}/mnt_work" 17 | mkdir -p "${MNT_WORK}" 18 | mkdir -p "${MNT_WORK}/container" 19 | mkdir -p "${MNT_WORK}/image" 20 | 21 | INAME="atomic-test-secret" 22 | } 23 | 24 | teardown () { 25 | rm -rf "${MNT_WORK}" 26 | } 27 | trap teardown EXIT 28 | 29 | setup 30 | 31 | id=`${DOCKER} create ${INAME} /bin/true` 32 | 33 | cleanup_container () { 34 | ${DOCKER} rm ${id} 35 | teardown 36 | } 37 | trap cleanup_container EXIT 38 | 39 | ${ATOMIC} mount ${id} ${MNT_WORK}/container 40 | ${ATOMIC} mount ${INAME} ${MNT_WORK}/image 41 | 42 | cleanup_mount () { 43 | ${ATOMIC} unmount ${MNT_WORK}/container 44 | ${ATOMIC} unmount ${MNT_WORK}/image 45 | cleanup_container 46 | } 47 | trap cleanup_mount EXIT 48 | 49 | # Expect failure 50 | set +e 51 | ${ATOMIC} mount ${id} --live ${MNT_WORK}/container 52 | if [ "$?" -eq "0" ]; then 53 | exit 1 54 | fi 55 | ${ATOMIC} mount ${INAME} --live ${MNT_WORK}/image 56 | if [ "$?" -eq "0" ]; then 57 | exit 1 58 | fi 59 | 60 | set -e 61 | 62 | ${ATOMIC} unmount ${MNT_WORK}/container 63 | ${ATOMIC} mount ${id} --shared ${MNT_WORK}/container 64 | 65 | if [[ "`cat "${MNT_WORK}/container/secret"`" != "${SECRET}" ]]; then 66 | exit 1 67 | fi 68 | if [[ "`cat "${MNT_WORK}/image/secret"`" != "${SECRET}" ]]; then 69 | exit 1 70 | fi 71 | 72 | running_cont=$(${DOCKER} run -d centos sleep infinity) 73 | 74 | cleanup_working_cont () { 75 | cleanup_mount 76 | ${DOCKER} rm -f ${running_cont} 77 | } 78 | trap cleanup_working_cont EXIT 79 | 80 | ${ATOMIC} unmount ${MNT_WORK}/container 81 | ${ATOMIC} mount ${running_cont} --live ${MNT_WORK}/container 82 | 83 | touch ${MNT_WORK}/container/we_can_write_here 84 | ${DOCKER} cp ${running_cont}:/we_can_write_here ${MNT_WORK}/ 85 | -------------------------------------------------------------------------------- /tests/integration/test_params.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | ATOMIC=$(grep -v -- --debug <<< "$ATOMIC") 5 | 6 | test() { 7 | expected=$(for i in $@; do echo $i; done) 8 | output=$(${ATOMIC} install atomic-test-6 $@ | grep -v docker) 9 | 10 | if [[ "${expected}" != "${output}" ]]; then 11 | # Test failed 12 | echo "Test Failed" 13 | echo ${expected} 14 | echo "!= " 15 | echo ${output} 16 | exit 1 17 | fi 18 | } 19 | 20 | # Test scripts run with PWD=tests/.. 21 | 22 | # The test harness exports some variables into the environment during 23 | # testing: PYTHONPATH (python module import path 24 | # WORK_DIR (a directory that is safe to modify) 25 | # DOCKER (the docker executable location) 26 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 27 | # SECRET (a generated sha256 hash inserted into test containers) 28 | 29 | # In addition, the test harness creates some images for use in testing. 30 | # See tests/test-images/ 31 | 32 | OUTPUT=$(/bin/true) 33 | 34 | rc=0 35 | # Test different values 36 | test 'asdf&jkl' 37 | test 'bash -c "echo arg1 arg2"' 38 | test ježek 'asdf jkl' 39 | 40 | -------------------------------------------------------------------------------- /tests/integration/test_pass.sh: -------------------------------------------------------------------------------- 1 | exit 0 2 | -------------------------------------------------------------------------------- /tests/integration/test_printenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # Test scripts run with PWD=tests/.. 6 | 7 | # The test harness exports some variables into the environment during 8 | # testing: PYTHONPATH (python module import path 9 | # WORK_DIR (a directory that is safe to modify) 10 | # DOCKER (the docker executable location) 11 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 12 | # SECRET (a generated sha256 hash inserted into test containers) 13 | 14 | TESTDIR=/run/atomic/test 15 | 16 | mkdir -p ${TESTDIR} 17 | 18 | # In addition, the test harness creates some images for use in testing. 19 | # See tests/test-images/ 20 | 21 | teardown () { 22 | # Cleanup your test data. 23 | set +e 24 | rm -rf ${TESTDIR} 25 | set -e 26 | } 27 | 28 | # Utilize exit traps for cleanup wherever possible. Additional cleanup 29 | # logic can be added to a "cleanup stack", by cascading function calls 30 | # within traps. See tests/integration/test_mount.sh for an example. 31 | trap teardown EXIT 32 | 33 | OUTPUT=$(/bin/true) 34 | 35 | cd $(realpath $PWD) 36 | 37 | ${ATOMIC} run atomic-test-5 | grep -v ^NAME= | grep -v ^IMAGE= | grep -v ^SUDO | grep -v printenv | grep -v atomic | grep -v coverage | sort > ${TESTDIR}/atomic-test-5.1 38 | 39 | printenv | grep -v printenv | grep -v ^SUDO | grep -v atomic | grep -v coverage | sort > ${TESTDIR}/atomic-test-5.2 40 | diff ${TESTDIR}/atomic-test-5.1 ${TESTDIR}/atomic-test-5.2 41 | exit $? 42 | 43 | -------------------------------------------------------------------------------- /tests/integration/test_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | ATOMIC=${ATOMIC:="/usr/bin/atomic"} 6 | ATOMIC=$(grep -v -- --debug <<< "$ATOMIC") 7 | DOCKER=${DOCKER:="/usr/bin/docker"} 8 | 9 | teardown () { 10 | set +e 11 | ${DOCKER} rm -f RUN_TEST > /dev/null 12 | set -e 13 | } 14 | 15 | fail () { 16 | echo "Fail: TEST ${1} should have failed and did not" 17 | exit 1 18 | } 19 | 20 | passed () { 21 | echo "Passed: TEST ${1}" 22 | } 23 | 24 | 25 | failed () { 26 | echo "Fail: TEST ${1}" 27 | exit 1 28 | } 29 | 30 | 31 | trap teardown EXIT 32 | 33 | # Check that atomic run's naming 34 | TEST_NUM=1 35 | ${ATOMIC} run -n RUN_TEST atomic-test-1 date 36 | CID=$("$DOCKER" ps -alq) 37 | NAME=$("$DOCKER" inspect --format='{{.Name}}' "$CID") 38 | 39 | 40 | if [[ "${NAME}" != /RUN_TEST ]]; then 41 | failed "${TEST_NUM}" 42 | fi 43 | passed "${TEST_NUM}" 44 | 45 | TEST_NUM=2 46 | rc=0 47 | ${ATOMIC} run -n RUN_TEST atomic-test-1 date 1>/dev/null || rc=$? 48 | if [[ ${rc} != 1 ]]; then 49 | # Test failed 50 | fail "${TEST_NUM}" 51 | fi 52 | 53 | passed "${TEST_NUM}" 54 | 55 | 56 | TEST_NUM=3 57 | ${ATOMIC} run --replace -n RUN_TEST atomic-test-1 date 58 | NEW_CID=$("$DOCKER" ps -alq) 59 | NAME=$("$DOCKER" inspect --format='{{.Name}}' "$NEW_CID") 60 | 61 | if [[ "${NAME}" != /RUN_TEST ]]; then 62 | failed "${TEST_NUM}" 63 | fi 64 | 65 | if [[ "${NEW_CID}" == "${CID}" ]]; then 66 | failed "${TEST_NUM}" 67 | fi 68 | 69 | passed "${TEST_NUM}" 70 | -------------------------------------------------------------------------------- /tests/integration/test_run_opts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | test() { 6 | expected="$1" 7 | output=$(${ATOMIC} run atomic-test-6 | grep -v docker) 8 | 9 | if [[ "${expected}" != "${output}" ]]; then 10 | # Test failed 11 | echo "Test Failed" 12 | echo ${expected} 13 | echo "!= " 14 | echo ${output} 15 | exit 1 16 | fi 17 | } 18 | 19 | # Test scripts run with PWD=tests/.. 20 | 21 | # The test harness exports some variables into the environment during 22 | # testing: PYTHONPATH (python module import path 23 | # WORK_DIR (a directory that is safe to modify) 24 | # DOCKER (the docker executable location) 25 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 26 | # SECRET (a generated sha256 hash inserted into test containers) 27 | 28 | # In addition, the test harness creates some images for use in testing. 29 | # See tests/test-images/ 30 | 31 | OUTPUT=$(/bin/true) 32 | 33 | rc=0 34 | # Test different values 35 | echo --hostname=www.example.test -e VAR1=xyz > docker-run-opts-atomic-test-6 36 | test www.example.test:xyz 37 | echo --hostname=www2.example.test > docker-run-opts-atomic-test-6 38 | test www2.example.test: 39 | rm -f docker-run-opts-atomic-test-6 40 | 41 | -------------------------------------------------------------------------------- /tests/integration/test_system_containers_mount.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | . ./tests/integration/setup-scripts/system_containers_setup.sh 6 | 7 | # The mount test of system containers, covers: 8 | # 1. mounting an image 9 | # 2. mounting a container 10 | # 3. mount --live 11 | # 4. mount --shared 12 | 13 | setup () { 14 | mkdir ${WORK_DIR}/mount 15 | ${ATOMIC} pull --storage ostree docker:atomic-test-system:latest 16 | ${ATOMIC} install --system --name=${NAME} atomic-test-system 17 | } 18 | 19 | teardown () { 20 | set +o pipefail 21 | 22 | # Unmount the mountpoint 23 | ${ATOMIC} umount ${WORK_DIR}/mount &> /dev/null || true 24 | 25 | # Do not leave the runc container in any case 26 | runc kill $NAME 9 &> /dev/null || true 27 | runc delete $NAME &> /dev/null || true 28 | 29 | # Ensure there is no systemd service left running 30 | systemctl stop $NAME &> /dev/null || true 31 | systemctl disable $NAME &> /dev/null || true 32 | rm -rf /etc/systemd/system/${NAME}.service || true 33 | rm -rf /etc/tmpfiles.d/${NAME}.conf || true 34 | 35 | # Delete all images from ostree 36 | ostree --repo=${ATOMIC_OSTREE_REPO} refs --delete ociimage &> /dev/null || true 37 | } 38 | 39 | trap teardown EXIT 40 | 41 | OUTPUT=$(/bin/true) 42 | 43 | setup 44 | 45 | # 1. mount an image 46 | # Since we have atomic-test-system in both ostree and docker, test that user must specify 47 | OUTPUT=$(! ${ATOMIC} mount atomic-test-system ${WORK_DIR}/mount 2>&1) 48 | grep "Found more than one Image with name atomic-test-system" <<< $OUTPUT 49 | 50 | # Now specify a storage 51 | ${ATOMIC} mount atomic-test-system --storage ostree ${WORK_DIR}/mount 52 | test -e ${WORK_DIR}/mount/usr/bin/greet.sh 53 | ${ATOMIC} umount ${WORK_DIR}/mount 54 | test \! -e ${WORK_DIR}/mount/usr/bin/greet.sh 55 | 56 | 57 | # 2. mount a container 58 | ${ATOMIC} mount ${NAME} ${WORK_DIR}/mount 59 | test -e ${WORK_DIR}/mount/usr/bin/greet.sh 60 | ${ATOMIC} umount ${WORK_DIR}/mount 61 | 62 | 63 | # 3. Check that --live fails 64 | OUTPUT=$(! ${ATOMIC} mount --live ${NAME} ${WORK_DIR}/mount 2>&1) 65 | grep "do not support --live" <<< $OUTPUT 66 | 67 | 68 | # 4. Check that --shared works and that 'http:' is dropped 69 | ${ATOMIC} mount --shared http:${NAME} ${WORK_DIR}/mount 70 | test -e ${WORK_DIR}/mount/usr/bin/greet.sh 71 | ${ATOMIC} umount ${WORK_DIR}/mount 72 | -------------------------------------------------------------------------------- /tests/integration/test_system_containers_pull.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | . ./tests/integration/setup-scripts/system_containers_setup.sh 6 | 7 | # The pull test of system containers, mainly to 8 | # test out the new usage of skopeo copy, it covers 9 | # 1: pull dockertar with a custom name (dockertar:customimagename) 10 | # 2: pull dockertar with same image name (dockertar:image) 11 | # 3: pull from docker daemon (docker:) 12 | 13 | teardown() { 14 | set +o pipefail 15 | 16 | # For now, we only delete the refs from ostree 17 | ostree --repo=${ATOMIC_OSTREE_REPO} refs --delete ociimage &> /dev/null || true 18 | 19 | # Remove the generated tar file to avoid affecting other tests 20 | rm -rf ${WORK_DIR}/atomic-test-random-name || true 21 | rm -rf ${WORK_DIR}/atomic-test-system || true 22 | } 23 | 24 | check_image_existence() { 25 | image_name=$1; shift 26 | ref_name=$1; shift 27 | 28 | # Check for image appearance 29 | ${ATOMIC} images list -f type=ostree > ${WORK_DIR}/images.out 30 | assert_matches $image_name ${WORK_DIR}/images.out 31 | 32 | # Check for ostree refs 33 | ostree --repo=${ATOMIC_OSTREE_REPO} refs > ${WORK_DIR}/ostree_refs.out 34 | assert_matches $ref_name ${WORK_DIR}/ostree_refs.out 35 | } 36 | 37 | cleanup_image() { 38 | image_name=$1 39 | ${ATOMIC} --assumeyes images delete -f --storage ostree $image_name 40 | ${ATOMIC} images prune 41 | ${ATOMIC} images list -f type=ostree > ${WORK_DIR}/images.out 42 | assert_not_matches $image_name ${WORK_DIR}/images.out 43 | } 44 | 45 | trap teardown EXIT 46 | 47 | OUTPUT=$(/bin/true) 48 | 49 | # 1: Pull docker tar and check from image list 50 | docker save -o ${WORK_DIR}/atomic-test-random-name atomic-test-system 51 | ${ATOMIC} pull --storage ostree dockertar:/${WORK_DIR}/atomic-test-random-name 52 | check_image_existence "atomic-test-system" "ociimage/atomic-test-system_3Alatest" 53 | cleanup_image "atomic-test-system" 54 | 55 | # 2: Pull docker tar with default name and check image 56 | docker save atomic-test-system > ${WORK_DIR}/atomic-test-system 57 | ${ATOMIC} pull --storage ostree dockertar:/${WORK_DIR}/atomic-test-system 58 | check_image_existence "atomic-test-system" "ociimage/atomic-test-system_3Alatest" 59 | cleanup_image "atomic-test-system" 60 | 61 | # 3: Pull from local docker and check 62 | ${ATOMIC} pull --storage ostree docker:atomic-test-system:latest 63 | check_image_existence "atomic-test-system" "ociimage/atomic-test-system_3Alatest" 64 | cleanup_image "atomic-test-system" 65 | 66 | -------------------------------------------------------------------------------- /tests/integration/test_tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # 6 | # 'atomic images tag' integration tests 7 | # 8 | ATOMIC=${ATOMIC:="/usr/bin/atomic"} 9 | ATOMIC=$(grep -v -- --debug <<< "$ATOMIC") 10 | DOCKER=${DOCKER:="/usr/bin/docker"} 11 | 12 | teardown () { 13 | set +e 14 | ${DOCKER} rmi at1:latest 15 | set -e 16 | } 17 | 18 | failed () { 19 | echo "${1} should have failed and did not" 20 | } 21 | 22 | trap teardown EXIT 23 | 24 | rc=0 25 | NAME="TEST1" 26 | # Try to tag a non-existant image 27 | ${ATOMIC} images tag foobar123:latest f:latest 1>/dev/null || rc=$? 28 | if [[ ${rc} != 1 ]]; then 29 | # Test failed 30 | failed "${NAME}" 31 | exit 1 32 | fi 33 | 34 | rc=0 35 | 36 | # Try to tag a docker image to ostree should fail 37 | NAME="TEST2" 38 | ${ATOMIC} images tag --storage ostree atomic-test-1:latest at1:latest 1>/dev/null || rc=$? 39 | if [[ ${rc} != 1 ]]; then 40 | # Test failed 41 | failed "${NAME}" 42 | exit 1 43 | fi 44 | 45 | # Tag a docker image 46 | NAME="TEST3" 47 | ${ATOMIC} images tag atomic-test-1:latest at1:latest 48 | ${DOCKER} inspect at1:latest 1>/dev/null 49 | -------------------------------------------------------------------------------- /tests/integration/test_top.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # Test scripts run with PWD=tests/.. 6 | 7 | # The test harness exports some variables into the environment during 8 | # testing: PYTHONPATH (python module import path 9 | # WORK_DIR (a directory that is safe to modify) 10 | # DOCKER (the docker executable location) 11 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 12 | # SECRET (a generated sha256 hash inserted into test containers) 13 | 14 | # In addition, the test harness creates some images for use in testing. 15 | # See tests/test-images/ 16 | 17 | setup () { 18 | # Perform setup routines here. 19 | IMAGE="atomic-test-1" 20 | id1=`${DOCKER} run -d ${IMAGE} /usr/bin/vi` 21 | id2=`${DOCKER} run -d ${IMAGE} /usr/bin/top` 22 | 23 | } 24 | 25 | teardown () { 26 | # Cleanup your test data. 27 | set +e 28 | ${DOCKER} stop ${id1} 29 | ${DOCKER} rm ${id1} 30 | ${DOCKER} stop ${id2} 31 | ${DOCKER} rm ${id2} 32 | set -e 33 | } 34 | # Utilize exit traps for cleanup wherever possible. Additional cleanup 35 | # logic can be added to a "cleanup stack", by cascading function calls 36 | # within traps. See tests/integration/test_mount.sh for an example. 37 | trap teardown EXIT 38 | 39 | setup 40 | 41 | OUTPUT=$(/bin/true) 42 | 43 | ${ATOMIC} top -n 1 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /tests/integration/test_verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | # Test scripts run with PWD=tests/.. 6 | 7 | # The test harness exports some variables into the environment during 8 | # testing: PYTHONPATH (python module import path 9 | # WORK_DIR (a directory that is safe to modify) 10 | # DOCKER (the docker executable location) 11 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 12 | # SECRET (a generated sha256 hash inserted into test containers) 13 | 14 | # In addition, the test harness creates some images for use in testing. 15 | # See tests/test-images/ 16 | echo "testing" 17 | 18 | if ! ${ATOMIC} images --help | grep generate; then 19 | exit 77 20 | fi 21 | 22 | IMAGE="atomic-test-4" 23 | ID=`${DOCKER} inspect ${IMAGE} | grep '"Id"' | cut -f4 --delimiter=\"` 24 | ATOMIC_VAR_LIB='/var/lib/atomic' 25 | 26 | setup () { 27 | # Perform setup routines here. 28 | ${DOCKER} tag ${IMAGE} foobar/${IMAGE}:latest 29 | 30 | } 31 | 32 | teardown () { 33 | # Cleanup your test data. 34 | set +e 35 | ${DOCKER} rmi foobar/${IMAGE}:latest 36 | set -e 37 | } 38 | 39 | # Utilize exit traps for cleanup wherever possible. Additional cleanup 40 | # logic can be added to a "cleanup stack", by cascading function calls 41 | # within traps. See tests/integration/test_mount.sh for an example. 42 | trap teardown EXIT 43 | 44 | setup 45 | rc=0 46 | 47 | ${ATOMIC} verify ${ID} 1>/dev/null || rc=$? 48 | 49 | if [[ ${rc} != 1 ]]; then 50 | # Test failed 51 | echo "This test should result in a return code of 1" 52 | exit 1 53 | fi 54 | 55 | ${ATOMIC} images generate 56 | if [ ! -d ${ATOMIC_VAR_LIB}/gomtree-manifests ]; then 57 | echo "gomtree manifests not created" 58 | exit 1 59 | fi 60 | rm -rf ${ATOMIC_VAR_LIB}/gomtree-manifests 61 | -------------------------------------------------------------------------------- /tests/integration_template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -euo pipefail 3 | IFS = $'\n\t' 4 | 5 | # Test scripts run with PWD=tests/.. 6 | 7 | # The test harness exports some variables into the environment during 8 | # testing: PYTHONPATH (python module import path 9 | # WORK_DIR (a directory that is safe to modify) 10 | # DOCKER (the docker executable location) 11 | # ATOMIC (an invocation of 'atomic' which measures code coverage) 12 | # SECRET (a generated sha256 hash inserted into test containers) 13 | 14 | # In addition, the test harness creates some images for use in testing. 15 | # See tests/test-images/ 16 | 17 | setup () { 18 | # Perform setup routines here. 19 | true 20 | } 21 | 22 | teardown () { 23 | # Cleanup your test data. 24 | true 25 | } 26 | # Utilize exit traps for cleanup wherever possible. Additional cleanup 27 | # logic can be added to a "cleanup stack", by cascading function calls 28 | # within traps. See tests/integration/test_mount.sh for an example. 29 | trap teardown EXIT 30 | 31 | # To expect a command to fail, observe the following pattern: 32 | set +e # disable fail on error 33 | false 34 | if [[ $? -eq 0 ]]; then 35 | exit 1 36 | fi 37 | set -e # enable fail on error 38 | 39 | # The test is considered to pass if it exits with zero status. Any other 40 | # exit status is considered failure. 41 | 42 | OUTPUT=$(/bin/true) 43 | 44 | if [[ $? -ne 0 ]]; then 45 | exit 1 46 | fi 47 | -------------------------------------------------------------------------------- /tests/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | 4 | "tools": { 5 | "timezone": { 6 | "label": "Atomic", 7 | "path": "atomicClient.html" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.1: -------------------------------------------------------------------------------- 1 | FROM centos 2 | MAINTAINER "Sally O'Malley 3 | ENV container docker 4 | 5 | 6 | LABEL RUN "/usr/bin/docker run -t --user \${SUDO_UID}:\${SUDO_GID} \${OPT1} -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the run label." 7 | 8 | LABEL INSTALL "/usr/bin/docker \${OPT1} run -v /etc/\${NAME}:/etc -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the install label." 9 | 10 | LABEL UNINSTALL "/usr/bin/docker \${OPT1} run -v /etc/\${NAME}:/etc -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the uninstall label." 11 | 12 | COPY help.1 / 13 | 14 | LABEL "Name"="atomic-test-1" 15 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.3: -------------------------------------------------------------------------------- 1 | FROM centos 2 | MAINTAINER "Sally O'Malley 3 | ENV container docker 4 | 5 | LABEL RUN "/usr/bin/docker run -t --user \${SUDO_UID}:\${SUDO_GID} \${OPT1} -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the run label." 6 | 7 | LABEL INSTALL "/usr/bin/docker \${OPT1} run -v /etc/\${NAME}:/etc -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the install label." 8 | 9 | COPY help.sh / 10 | 11 | LABEL HELP "docker run --rm IMAGE /bin/bash /help.sh" 12 | 13 | LABEL "Name"="atomic-test-3" 14 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.4: -------------------------------------------------------------------------------- 1 | FROM centos 2 | MAINTAINER "Sally O'Malley 3 | ENV container docker 4 | 5 | LABEL RUN "/usr/bin/docker run -t --user \${SUDO_UID}:\${SUDO_GID} \${OPT1} -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the run label." 6 | 7 | LABEL INSTALL "/usr/bin/docker \${OPT1} run -v /etc/\${NAME}:/etc -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the install label." 8 | 9 | COPY help.sh / 10 | 11 | LABEL help "docker run --rm IMAGE /usr/bin/bash /help.sh" 12 | 13 | LABEL "Name"="atomic-test-4" 14 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.5: -------------------------------------------------------------------------------- 1 | FROM centos 2 | LABEL "Name"="atomic-test-5" 3 | LABEL RUN printenv 4 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.6: -------------------------------------------------------------------------------- 1 | FROM centos 2 | COPY install.sh show-hostname.sh /bin/ 3 | RUN chmod +x /bin/install.sh /bin/show-hostname.sh 4 | LABEL INSTALL 'docker run --rm=true --net=host -v /:/host -e NAME=${NAME} -e IMAGE=${IMAGE} -e HOST=/host ${IMAGE} /bin/install.sh' 5 | LABEL "Name"="atomic-test-6" 6 | LABEL RUN_OPTS_FILE '$PWD/docker-run-opts-${NAME}' 7 | LABEL RUN 'docker run --rm=true ${RUN_OPTS} ${IMAGE} /bin/show-hostname.sh' 8 | LABEL STOP 'docker stop $NAME' 9 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.runonce: -------------------------------------------------------------------------------- 1 | FROM centos 2 | 3 | LABEL "Name"="atomic-test-runonce"\ 4 | "atomic.run"="once"\ 5 | "atomic.type"="system" 6 | 7 | ADD hi.sh /usr/bin/run.sh 8 | 9 | # Export the files used for the system container 10 | ADD manifest.json config.json.template /exports/ 11 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.system: -------------------------------------------------------------------------------- 1 | FROM centos 2 | RUN yum -y install nmap-ncat && yum clean all 3 | 4 | LABEL "Name"="atomic-test-system" 5 | 6 | ADD run.sh greet.sh /usr/bin/ 7 | 8 | # Export the files used for the system container 9 | ADD tmpfiles.template manifest.json service.template config.json.template /exports/ 10 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.system-hostfs: -------------------------------------------------------------------------------- 1 | FROM centos 2 | RUN yum -y install nmap-ncat && yum clean all 3 | 4 | LABEL "Name"="atomic-test-system-hostfs" 5 | LABEL "atomic.has_install_files"="yes" 6 | 7 | # Add a file that can be handled by the rpm generator 8 | RUN mkdir -p /exports/hostfs/usr/local/lib /exports/hostfs/usr/local/placeholder-lib 9 | RUN ln -s /does/not/exist /exports/hostfs/broken-symlink 10 | ADD message /exports/hostfs/usr/local/lib/secret-message 11 | ADD message-template /exports/hostfs/usr/local/lib/secret-message-template 12 | 13 | # this is going to be renamed 14 | ADD message /exports/hostfs/usr/local/lib/placeholder-file 15 | 16 | ADD run.sh greet.sh /usr/bin/ 17 | 18 | # Export the files used for the system container 19 | ADD tmpfiles.template manifest.json service.template config.json.template /exports/ 20 | -------------------------------------------------------------------------------- /tests/test-images/Dockerfile.system-update: -------------------------------------------------------------------------------- 1 | FROM centos 2 | RUN yum -y install nmap-ncat && yum clean all 3 | 4 | LABEL Name="atomic-test-system-update" \ 5 | summary="Updated system container image" \ 6 | atomic.type='system' 7 | 8 | ADD run.sh greet.sh /usr/bin/ 9 | 10 | # Export the files used for the system container 11 | COPY tmpfiles.template manifest.json service.template config.json.template /exports/ 12 | -------------------------------------------------------------------------------- /tests/test-images/help.1: -------------------------------------------------------------------------------- 1 | .TH "ATOMIC" "1" " Atomic Man Pages" "Brent Baude" "December 2015" "" 2 | 3 | 4 | .SH DESCRIPTION 5 | .PP 6 | Simple \ftest\fP case 7 | 8 | .TS 9 | allbox; 10 | l l 11 | l l . 12 | Simple Table 13 | Simple Cell 14 | .TE 15 | 16 | .SH USAGE 17 | .PP 18 | test 19 | 20 | .PP 21 | .RS 22 | 23 | .SH HISTORY 24 | .PP 25 | December 2015, Originally written by Brent Baude (bbaude at redhat dot com) 26 | -------------------------------------------------------------------------------- /tests/test-images/help.sh: -------------------------------------------------------------------------------- 1 | echo "Testing help" 2 | -------------------------------------------------------------------------------- /tests/test-images/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in "$@" ; do 3 | echo "$i" 4 | done 5 | -------------------------------------------------------------------------------- /tests/test-images/show-hostname.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo $HOSTNAME:$VAR1 3 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files-hostfs/config.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "ociVersion": "0.3.0", 3 | "platform": { 4 | "os": "linux", 5 | "arch": "amd64" 6 | }, 7 | "process": { 8 | "terminal": false, 9 | "user": { 10 | "uid": 0, 11 | "gid": 0 12 | }, 13 | "args": [ 14 | "/usr/bin/run.sh" 15 | ], 16 | "env": [ 17 | "PORT=$PORT", 18 | "RECEIVER=$RECEIVER", 19 | "UUID=$UUID", 20 | "NAME=$NAME", 21 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 22 | "TERM=xterm" 23 | ], 24 | "cwd": "/" 25 | }, 26 | "root": { 27 | "path": "rootfs", 28 | "readonly": true 29 | }, 30 | "mounts": [ 31 | { 32 | "destination": "/proc", 33 | "type": "proc", 34 | "source": "proc" 35 | }, 36 | { 37 | "destination": "/dev", 38 | "type": "tmpfs", 39 | "source": "tmpfs", 40 | "options": [ 41 | "nosuid", 42 | "strictatime", 43 | "mode=755", 44 | "size=65536k" 45 | ] 46 | }, 47 | { 48 | "destination": "/dev/pts", 49 | "type": "devpts", 50 | "source": "devpts", 51 | "options": [ 52 | "nosuid", 53 | "noexec", 54 | "newinstance", 55 | "ptmxmode=0666", 56 | "mode=0620", 57 | "gid=5" 58 | ] 59 | }, 60 | { 61 | "destination": "/dev/shm", 62 | "type": "tmpfs", 63 | "source": "shm", 64 | "options": [ 65 | "nosuid", 66 | "noexec", 67 | "nodev", 68 | "mode=1777", 69 | "size=65536k" 70 | ] 71 | }, 72 | { 73 | "destination": "/dev/mqueue", 74 | "type": "mqueue", 75 | "source": "mqueue", 76 | "options": [ 77 | "nosuid", 78 | "noexec", 79 | "nodev" 80 | ] 81 | }, 82 | { 83 | "destination": "/sys", 84 | "type": "sysfs", 85 | "source": "sysfs", 86 | "options": [ 87 | "nosuid", 88 | "noexec", 89 | "nodev" 90 | ] 91 | }, 92 | { 93 | "destination": "/sys/fs/cgroup", 94 | "type": "cgroup", 95 | "source": "cgroup", 96 | "options": [ 97 | "nosuid", 98 | "noexec", 99 | "nodev", 100 | "relatime", 101 | "ro" 102 | ] 103 | }, 104 | { 105 | "type": "bind", 106 | "source": "${RUN_DIRECTORY}/${NAME}", 107 | "destination": "/var/run/", 108 | "options": [ 109 | "rbind", 110 | "rw", 111 | "mode=755" 112 | ] 113 | } 114 | ], 115 | "hooks": {}, 116 | "linux": { 117 | "capabilities": [ 118 | "CAP_AUDIT_WRITE", 119 | "CAP_KILL", 120 | "CAP_NET_BIND_SERVICE" 121 | ], 122 | "rlimits": [ 123 | { 124 | "type": "RLIMIT_NOFILE", 125 | "hard": 1024, 126 | "soft": 1024 127 | } 128 | ], 129 | "resources": { 130 | "devices": [ 131 | { 132 | "allow": false, 133 | "access": "rwm" 134 | } 135 | ] 136 | }, 137 | "namespaces": [ 138 | { 139 | "type": "pid" 140 | }, 141 | { 142 | "type": "ipc" 143 | }, 144 | { 145 | "type": "mount" 146 | } 147 | ], 148 | "devices": null, 149 | "apparmorProfile": "", 150 | "selinuxProcessLabel": "", 151 | "seccomp": { 152 | "defaultAction": "", 153 | "architectures": null 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files-hostfs/greet.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | printf "HTTP/1.1 200 OK\r\n" 4 | printf "Connection: Close\r\n" 5 | printf "\r\n" 6 | 7 | printf "Hi $RECEIVER from container $NAME with UUID=$UUID\r\n" 8 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files-hostfs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultValues": { 4 | "PORT": "8081", 5 | "RECEIVER": "World" 6 | }, 7 | "renameFiles" : { 8 | "/usr/local/lib/placeholder-file" : "/usr/local/lib/renamed-$NAME" 9 | }, 10 | "installedFilesTemplate" : [ 11 | "/usr/local/lib/secret-message-template" 12 | ], 13 | "useLinks" : false 14 | } 15 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files-hostfs/message: -------------------------------------------------------------------------------- 1 | Hello unknown $RECEIVER 2 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files-hostfs/message-template: -------------------------------------------------------------------------------- 1 | hello $RECEIVER 2 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files-hostfs/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | nc --verbose -k -l ${PORT:-8081} --sh-exec /usr/bin/greet.sh & 4 | 5 | cleanup () 6 | { 7 | kill -9 $! 8 | exit 0 9 | } 10 | 11 | trap cleanup SIGINT SIGTERM 12 | 13 | wait $! 14 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files-hostfs/service.template: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Hello World System Container 3 | 4 | [Service] 5 | ExecStart=$EXEC_START 6 | ExecStop=$EXEC_STOP 7 | Restart=on-failure 8 | WorkingDirectory=$DESTDIR 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | 13 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files-hostfs/tmpfiles.template: -------------------------------------------------------------------------------- 1 | D ${RUN_DIRECTORY}/${NAME} 0700 - - - - 2 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files/config.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "ociVersion": "0.3.0", 3 | "platform": { 4 | "os": "linux", 5 | "arch": "amd64" 6 | }, 7 | "process": { 8 | "terminal": false, 9 | "user": { 10 | "uid": 0, 11 | "gid": 0 12 | }, 13 | "args": [ 14 | "/usr/bin/run.sh" 15 | ], 16 | "env": [ 17 | "PORT=$PORT", 18 | "RECEIVER=$RECEIVER", 19 | "UUID=$UUID", 20 | "NAME=$NAME", 21 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 22 | "TERM=xterm" 23 | ], 24 | "cwd": "/" 25 | }, 26 | "root": { 27 | "path": "rootfs", 28 | "readonly": true 29 | }, 30 | "mounts": [ 31 | { 32 | "destination": "/proc", 33 | "type": "proc", 34 | "source": "proc" 35 | }, 36 | { 37 | "destination": "/dev", 38 | "type": "tmpfs", 39 | "source": "tmpfs", 40 | "options": [ 41 | "nosuid", 42 | "strictatime", 43 | "mode=755", 44 | "size=65536k" 45 | ] 46 | }, 47 | { 48 | "destination": "/dev/pts", 49 | "type": "devpts", 50 | "source": "devpts", 51 | "options": [ 52 | "nosuid", 53 | "noexec", 54 | "newinstance", 55 | "ptmxmode=0666", 56 | "mode=0620", 57 | "gid=5" 58 | ] 59 | }, 60 | { 61 | "destination": "/dev/shm", 62 | "type": "tmpfs", 63 | "source": "shm", 64 | "options": [ 65 | "nosuid", 66 | "noexec", 67 | "nodev", 68 | "mode=1777", 69 | "size=65536k" 70 | ] 71 | }, 72 | { 73 | "destination": "/dev/mqueue", 74 | "type": "mqueue", 75 | "source": "mqueue", 76 | "options": [ 77 | "nosuid", 78 | "noexec", 79 | "nodev" 80 | ] 81 | }, 82 | { 83 | "destination": "/sys", 84 | "type": "sysfs", 85 | "source": "sysfs", 86 | "options": [ 87 | "nosuid", 88 | "noexec", 89 | "nodev" 90 | ] 91 | }, 92 | { 93 | "destination": "/sys/fs/cgroup", 94 | "type": "cgroup", 95 | "source": "cgroup", 96 | "options": [ 97 | "nosuid", 98 | "noexec", 99 | "nodev", 100 | "relatime", 101 | "ro" 102 | ] 103 | }, 104 | { 105 | "type": "bind", 106 | "source": "${RUN_DIRECTORY}/${NAME}", 107 | "destination": "/var/run/", 108 | "options": [ 109 | "rbind", 110 | "rw", 111 | "mode=755" 112 | ] 113 | } 114 | ], 115 | "hooks": {}, 116 | "linux": { 117 | "capabilities": { 118 | "bounding": [${ALL_PROCESS_CAPABILITIES}] 119 | }, 120 | "rlimits": [ 121 | { 122 | "type": "RLIMIT_NOFILE", 123 | "hard": 1024, 124 | "soft": 1024 125 | } 126 | ], 127 | "resources": { 128 | "devices": [ 129 | { 130 | "allow": false, 131 | "access": "rwm" 132 | } 133 | ] 134 | }, 135 | "namespaces": [ 136 | { 137 | "type": "pid" 138 | }, 139 | { 140 | "type": "ipc" 141 | }, 142 | { 143 | "type": "mount" 144 | } 145 | ], 146 | "devices": null, 147 | "apparmorProfile": "", 148 | "selinuxProcessLabel": "", 149 | "seccomp": { 150 | "defaultAction": "", 151 | "architectures": null 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files/greet.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | printf "HTTP/1.1 200 OK\r\n" 4 | printf "Connection: Close\r\n" 5 | printf "\r\n" 6 | 7 | printf "Hi $RECEIVER from container $NAME with UUID=$UUID\r\n" 8 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultValues": { 4 | "PORT": "8081", 5 | "RECEIVER": "World" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | nc --verbose -k -l ${PORT:-8081} --sh-exec /usr/bin/greet.sh & 4 | 5 | cleanup () 6 | { 7 | kill -9 $! 8 | exit 0 9 | } 10 | 11 | trap cleanup SIGINT SIGTERM 12 | 13 | wait $! 14 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files/service.template: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Hello World System Container 3 | 4 | [Service] 5 | ExecStartPre=$EXEC_STARTPRE 6 | ExecStart=$EXEC_START 7 | ExecStop=$EXEC_STOP 8 | ExecStopPost=$EXEC_STOPPOST 9 | Restart=on-crash 10 | WorkingDirectory=$DESTDIR 11 | PIDFile=$PIDFILE 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | 16 | -------------------------------------------------------------------------------- /tests/test-images/system-container-files/tmpfiles.template: -------------------------------------------------------------------------------- 1 | D ${RUN_DIRECTORY}/${NAME} 0700 - - - - 2 | -------------------------------------------------------------------------------- /tests/test-images/system-container-runonce-files/config.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "ociVersion": "0.6.0-dev", 3 | "platform": { 4 | "os": "linux", 5 | "arch": "amd64" 6 | }, 7 | 8 | "process": { 9 | "terminal": false, 10 | "user": { 11 | "uid": 0, 12 | "gid": 0 13 | }, 14 | "args": [ 15 | "/usr/bin/run.sh" 16 | ], 17 | "env": [ 18 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 19 | "TERM=xterm", 20 | "NAME=$NAME", 21 | "RECEIVER=$RECEIVER" 22 | ], 23 | "cwd": "/" 24 | }, 25 | "root": { 26 | "path": "rootfs", 27 | "readonly": false 28 | }, 29 | "hostname": "acme", 30 | "mounts": [ 31 | { 32 | "destination": "/proc", 33 | "type": "proc", 34 | "source": "proc" 35 | }, 36 | { 37 | "destination": "/dev", 38 | "type": "tmpfs", 39 | "source": "tmpfs", 40 | "options": [ 41 | "nosuid", 42 | "strictatime", 43 | "mode=755", 44 | "size=65536k" 45 | ] 46 | }, 47 | { 48 | "destination": "/dev/pts", 49 | "type": "devpts", 50 | "source": "devpts", 51 | "options": [ 52 | "nosuid", 53 | "noexec", 54 | "newinstance", 55 | "ptmxmode=0666", 56 | "mode=0620", 57 | "gid=5" 58 | ] 59 | }, 60 | { 61 | "destination": "/dev/shm", 62 | "type": "tmpfs", 63 | "source": "shm", 64 | "options": [ 65 | "nosuid", 66 | "noexec", 67 | "nodev", 68 | "mode=1777", 69 | "size=65536k" 70 | ] 71 | }, 72 | { 73 | "destination": "/dev/mqueue", 74 | "type": "mqueue", 75 | "source": "mqueue", 76 | "options": [ 77 | "nosuid", 78 | "noexec", 79 | "nodev" 80 | ] 81 | }, 82 | { 83 | "destination": "/sys", 84 | "type": "sysfs", 85 | "source": "sysfs", 86 | "options": [ 87 | "nosuid", 88 | "noexec", 89 | "nodev", 90 | "ro" 91 | ] 92 | }, 93 | { 94 | "destination": "/sys/fs/cgroup", 95 | "type": "cgroup", 96 | "source": "cgroup", 97 | "options": [ 98 | "nosuid", 99 | "noexec", 100 | "nodev", 101 | "relatime", 102 | "ro" 103 | ] 104 | } 105 | ], 106 | "hooks": {}, 107 | "linux": { 108 | "resources": { 109 | "devices": [ 110 | { 111 | "allow": false, 112 | "access": "rwm" 113 | } 114 | ] 115 | }, 116 | "namespaces": [ 117 | { 118 | "type": "network" 119 | }, 120 | { 121 | "type": "pid" 122 | }, 123 | { 124 | "type": "mount" 125 | }, 126 | { 127 | "type": "ipc" 128 | }, 129 | { 130 | "type": "uts" 131 | } 132 | ] 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /tests/test-images/system-container-runonce-files/hi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "HI $RECEIVER from $NAME" 4 | -------------------------------------------------------------------------------- /tests/test-images/system-container-runonce-files/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultValues": {} 4 | } 5 | -------------------------------------------------------------------------------- /tests/test-images/system-container-update-files/config.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "ociVersion": "1.0.0", 3 | "platform": { 4 | "os": "linux", 5 | "arch": "amd64" 6 | }, 7 | "process": { 8 | "terminal": false, 9 | "user": { 10 | "uid": 0, 11 | "gid": 0 12 | }, 13 | "args": [ 14 | "/usr/bin/run.sh" 15 | ], 16 | "env": [ 17 | "PORT=$PORT", 18 | "UUID=$UUID", 19 | "NAME=$NAME", 20 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 21 | "TERM=xterm", 22 | "VAR_WITH_NO_DEFAULT=$VAR_WITH_NO_DEFAULT" 23 | ], 24 | "cwd": "/", 25 | "rlimits": [ 26 | { 27 | "type": "RLIMIT_NOFILE", 28 | "hard": 1024, 29 | "soft": 1024 30 | } 31 | ] 32 | }, 33 | "root": { 34 | "path": "rootfs", 35 | "readonly": true 36 | }, 37 | "mounts": [ 38 | { 39 | "destination": "/proc", 40 | "type": "proc", 41 | "source": "proc" 42 | }, 43 | { 44 | "destination": "/dev", 45 | "type": "tmpfs", 46 | "source": "tmpfs", 47 | "options": [ 48 | "nosuid", 49 | "strictatime", 50 | "mode=755", 51 | "size=65536k" 52 | ] 53 | }, 54 | { 55 | "destination": "/dev/pts", 56 | "type": "devpts", 57 | "source": "devpts", 58 | "options": [ 59 | "nosuid", 60 | "noexec", 61 | "newinstance", 62 | "ptmxmode=0666", 63 | "mode=0620", 64 | "gid=5" 65 | ] 66 | }, 67 | { 68 | "destination": "/dev/shm", 69 | "type": "tmpfs", 70 | "source": "shm", 71 | "options": [ 72 | "nosuid", 73 | "noexec", 74 | "nodev", 75 | "mode=1777", 76 | "size=65536k" 77 | ] 78 | }, 79 | { 80 | "destination": "/dev/mqueue", 81 | "type": "mqueue", 82 | "source": "mqueue", 83 | "options": [ 84 | "nosuid", 85 | "noexec", 86 | "nodev" 87 | ] 88 | }, 89 | { 90 | "destination": "/sys", 91 | "type": "sysfs", 92 | "source": "sysfs", 93 | "options": [ 94 | "nosuid", 95 | "noexec", 96 | "nodev" 97 | ] 98 | }, 99 | { 100 | "destination": "/sys/fs/cgroup", 101 | "type": "cgroup", 102 | "source": "cgroup", 103 | "options": [ 104 | "nosuid", 105 | "noexec", 106 | "nodev", 107 | "relatime", 108 | "ro" 109 | ] 110 | }, 111 | { 112 | "type": "bind", 113 | "source": "${RUN_DIRECTORY}/${NAME}", 114 | "destination": "/var/run/", 115 | "options": [ 116 | "rbind", 117 | "rw", 118 | "mode=755" 119 | ] 120 | } 121 | ], 122 | "hooks": {}, 123 | "linux": { 124 | "resources": { 125 | "devices": [ 126 | { 127 | "allow": false, 128 | "access": "rwm" 129 | } 130 | ] 131 | }, 132 | "namespaces": [ 133 | { 134 | "type": "pid" 135 | }, 136 | { 137 | "type": "ipc" 138 | }, 139 | { 140 | "type": "mount" 141 | } 142 | ], 143 | "devices": null, 144 | "apparmorProfile": "", 145 | "selinuxProcessLabel": "" 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /tests/test-images/system-container-update-files/greet.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | printf "HTTP/1.1 200 OK\r\n" 4 | printf "Connection: Close\r\n" 5 | printf "\r\n" 6 | 7 | printf "Hi from $VAR_WITH_NO_DEFAULT\r\n" 8 | -------------------------------------------------------------------------------- /tests/test-images/system-container-update-files/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultValues": { 4 | "PORT": "8081" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/test-images/system-container-update-files/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | nc --verbose -k -l ${PORT:-8081} --sh-exec /usr/bin/greet.sh & 4 | 5 | cleanup () 6 | { 7 | kill -9 $! 8 | exit 0 9 | } 10 | 11 | trap cleanup SIGINT SIGTERM 12 | 13 | wait $! 14 | -------------------------------------------------------------------------------- /tests/test-images/system-container-update-files/service.template: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Hello World System Container 3 | 4 | [Service] 5 | ExecStart=$EXEC_START 6 | ExecStop=$EXEC_STOP 7 | Restart=on-failure 8 | WorkingDirectory=$DESTDIR 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | 13 | -------------------------------------------------------------------------------- /tests/test-images/system-container-update-files/tmpfiles.template: -------------------------------------------------------------------------------- 1 | D ${RUN_DIRECTORY}/${NAME} 0700 - - - - 2 | -------------------------------------------------------------------------------- /tests/unit/fixtures/atomic.conf: -------------------------------------------------------------------------------- 1 | # Atomic CLI configuration file 2 | 3 | default_scanner: 4 | default_docker: docker 5 | # relative path, must run tests from repo base dir 6 | registry_confdir: tests/unit/fixtures/etc/containers/registries.d 7 | 8 | 9 | # Default storage backend [ostree, docker] 10 | # default_storage: ostree 11 | # ostree_repository: /ostree/repo 12 | # checkout_path: /var/lib/containers/atomic 13 | # 14 | 15 | # Default identity for signing images 16 | # default_signer: 17 | -------------------------------------------------------------------------------- /tests/unit/fixtures/configs/docker.io-repo.yaml: -------------------------------------------------------------------------------- 1 | docker: 2 | docker.io/repo: 3 | sigstore: https://sigstore.acme.com/sigs 4 | -------------------------------------------------------------------------------- /tests/unit/fixtures/configs/docker.io.updated.yaml: -------------------------------------------------------------------------------- 1 | docker: 2 | docker.io: 3 | sigstore: https://sigstore.example.com/update 4 | -------------------------------------------------------------------------------- /tests/unit/fixtures/configs/docker.io.yaml: -------------------------------------------------------------------------------- 1 | docker: 2 | docker.io: 3 | sigstore: https://sigstore.example.com/sigs 4 | -------------------------------------------------------------------------------- /tests/unit/fixtures/default_policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": [ 3 | { 4 | "type": "insecureAcceptAnything" 5 | } 6 | ], 7 | "transports": { 8 | "docker-daemon": { 9 | "": [ 10 | { 11 | "type": "insecureAcceptAnything" 12 | } 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/unit/fixtures/etc/containers/policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": [ 3 | { 4 | "type": "insecureAcceptAnything" 5 | } 6 | ], 7 | "transports": { 8 | "docker-daemon": { 9 | "": [ 10 | { 11 | "type": "insecureAcceptAnything" 12 | } 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/unit/fixtures/etc/containers/registries.d/docker.io-centos.yaml: -------------------------------------------------------------------------------- 1 | docker: 2 | docker.io/centos: 3 | sigstore: https://centos.org/sigstore/ 4 | -------------------------------------------------------------------------------- /tests/unit/fixtures/etc/containers/registries.d/registry.access.redhat.com.yaml: -------------------------------------------------------------------------------- 1 | docker: 2 | registry.access.redhat.com: 3 | sigstore: https://cdn.redhat.com/containers/signatures 4 | -------------------------------------------------------------------------------- /tests/unit/fixtures/key1.pub: -------------------------------------------------------------------------------- 1 | pub 4096R/FD431D51 2009-10-22 2 | Key fingerprint = 567E 347A D004 4ADE 55BA 8A5F 199E 2F91 FD43 1D51 3 | uid Red Hat, Inc. (release key 2) 4 | 5 | -----BEGIN PGP PUBLIC KEY BLOCK----- 6 | Version: GnuPG v1.4.5 (GNU/Linux) 7 | 8 | mQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF 9 | 0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF 10 | 0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c 11 | u7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh 12 | XGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H 13 | 5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW 14 | 9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj 15 | /DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1 16 | PcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY 17 | HVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF 18 | buhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB 19 | tDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0 20 | LmNvbT6JAjYEEwECACAFAkrgSTsCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK 21 | CRAZni+R/UMdUWzpD/9s5SFR/ZF3yjY5VLUFLMXIKUztNN3oc45fyLdTI3+UClKC 22 | 2tEruzYjqNHhqAEXa2sN1fMrsuKec61Ll2NfvJjkLKDvgVIh7kM7aslNYVOP6BTf 23 | C/JJ7/ufz3UZmyViH/WDl+AYdgk3JqCIO5w5ryrC9IyBzYv2m0HqYbWfphY3uHw5 24 | un3ndLJcu8+BGP5F+ONQEGl+DRH58Il9Jp3HwbRa7dvkPgEhfFR+1hI+Btta2C7E 25 | 0/2NKzCxZw7Lx3PBRcU92YKyaEihfy/aQKZCAuyfKiMvsmzs+4poIX7I9NQCJpyE 26 | IGfINoZ7VxqHwRn/d5mw2MZTJjbzSf+Um9YJyA0iEEyD6qjriWQRbuxpQXmlAJbh 27 | 8okZ4gbVFv1F8MzK+4R8VvWJ0XxgtikSo72fHjwha7MAjqFnOq6eo6fEC/75g3NL 28 | Ght5VdpGuHk0vbdENHMC8wS99e5qXGNDued3hlTavDMlEAHl34q2H9nakTGRF5Ki 29 | JUfNh3DVRGhg8cMIti21njiRh7gyFI2OccATY7bBSr79JhuNwelHuxLrCFpY7V25 30 | OFktl15jZJaMxuQBqYdBgSay2G0U6D1+7VsWufpzd/Abx1/c3oi9ZaJvW22kAggq 31 | dzdA27UUYjWvx42w9menJwh/0jeQcTecIUd0d0rFcw/c1pvgMMl/Q73yzKgKYw== 32 | =zbHE 33 | -----END PGP PUBLIC KEY BLOCK----- 34 | 35 | -------------------------------------------------------------------------------- /tests/unit/fixtures/key2.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v2 3 | 4 | mQENBFf9p8wBCACaGbvvPfzoGXA8KIAKkaFMR1gYHfxR8vWw7X8IfUFpQBpsIRcz 5 | rjsSnRY9me4Qzk5xTkMU5XJqQ9flMuqr6T2lASeA1qQRoZSaP5/nytHwmKkSt9O7 6 | HaVXjXOprxi4g/LZYfiuzBcxYMIQfOgXDJyYkdOa7oD/2Tp4fB4hXorfE3Mgybg6 7 | 3utPjaTtwjxt0+4atfQt7/GJLzPWo/g6xO8VXxb+WTkllGaoq2eoJ+Jw2IL5IN6U 8 | awK5wvGY29rvFn9y9V2KmnvnpziRYR40XQyef2kTvgALcYOUlA+6ZVKYH/VFv0PO 9 | lSOkNHrkuCHfbRJ3cc1e9at2GHd9Im44E7TBABEBAAG0CUJpbGx5IEJvYokBNwQT 10 | AQgAIQUCV/2nzAIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRCFdONrHaDy 11 | HZyfB/0ZjKTAWZ83BpD3cotuNbiWvznNUZu6BpkQM1/felBdEkLOURJqnwgFmWxJ 12 | FlklSKPqT4+5qIkGhNdSdP0PPz/3EoVWlRCwgxUdtFsX6D/BfpEwpQN8FMnMOWHb 13 | G4dS8gloAkFq3gNHxXmjXJn44OCto/QxcWKaRyTE5DJK9ufKZ937sIXUxHaz7qFw 14 | Yal2ZdqEVVmMi1SjkQ2gplEkDYcGjwokfHkljhp1UBIlAdkTb4FvGeBamTSkOlHZ 15 | WpqSX7XTmCdIcbpOb1XwRyIITW9vxbsuP+xXVsrTSHX/TviIy7Eh6y74OnX5WHlQ 16 | Ian/FR9h0ZZUNT5jEpKoQkcahOyFuQENBFf9p8wBCACkgKOoldPCfvP0i9IlBZ5h 17 | o9Z6mrEoaYpUxtcM4B2iK89U7c7chUcQqWd6UP0EuBIkF4jsGXZ3nlJqPqMtPqjX 18 | mODybjCekTPDBJHwzUkzQAWpfGgHaB9SD3OOQcvjQzMq7NaoA+h/bJGcPRPAfR9t 19 | fkpFrxdAD+L8ggUy50VyVIqskovNRbOUFP/0n+CEtRI4BCKjBEGvBdWjEXH6ks9f 20 | HioF7mQHjaKZKmrf+eqdBfJdM15wAUxHDST915EFVSmviuKnLR+k1z3NwTYbaEKb 21 | cibKzEs9PzZbYMPj5ltXLZQwErKbVTZyvnc6UFn/LxRiKdiOigJgxjrY2A86w8vx 22 | ABEBAAGJAR8EGAEIAAkFAlf9p8wCGwwACgkQhXTjax2g8h2KTAf6ArNqe2KAO7xA 23 | k33IR/R8g2blYpiI23ZbYjGIAuw9ZlWozfYu+nwQXBXxJFwBSaM4XZkQNG1+qtD4 24 | ohDIfONn5gNqF/ORS+kFXyw64qPdJOZX8P0/iBYCCrYxmGYvmo3F94QcZT9LMHar 25 | 74gtEB3ekij2bQ4+CEvikVGb3W/PV93rCG5mVNczZS1UhEVqiD9+cdLVypxwOExV 26 | OA3vJfvuepMQv38EB1k6nunOPa2hp4QFvrxqWM1LxfVzrRiN2+SYOUMEq0baekWz 27 | rOKbhaFnWhijrJYCt8rl69n1jbE1FnGRh9wGTXPcGR2NHZ0NOPcvxE4fiFn8bIT5 28 | osnt4ovetA== 29 | =yaT2 30 | -----END PGP PUBLIC KEY BLOCK----- 31 | -------------------------------------------------------------------------------- /tests/unit/fixtures/show_policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": [ 3 | { 4 | "type": "insecureAcceptAnything" 5 | } 6 | ], 7 | "transports": { 8 | "docker": { 9 | "docker.io": [ 10 | { 11 | "type": "reject" 12 | } 13 | ], 14 | "registry.access.redhat.com": [ 15 | { 16 | "keyPath": "tests/unit/fixtures/key1.pub", 17 | "keyType": "GPGKeys", 18 | "type": "signedBy" 19 | } 20 | ], 21 | "docker.io/centos": [ 22 | { 23 | "keyPath": "tests/unit/fixtures/key2.pub", 24 | "keyType": "GPGKeys", 25 | "type": "signedBy" 26 | } 27 | ] 28 | }, 29 | "atomic": { 30 | "private.example.com": [ 31 | { 32 | "keyPath": "tests/unit/fixtures/key2.pub", 33 | "keyType": "GPGKeys", 34 | "type": "signedBy" 35 | } 36 | ] 37 | }, 38 | "dir": { 39 | "localhost": [ 40 | { 41 | "keyPath": "tests/unit/fixtures/key2.pub", 42 | "keyType": "GPGKeys", 43 | "type": "signedBy" 44 | } 45 | ] 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/unit/fixtures/show_policy.output: -------------------------------------------------------------------------------- 1 | * (default) accept 2 | docker.io reject 3 | docker.io/centos signed Billy Bob https://centos.org/sigstore/ 4 | localhost signed Billy Bob 5 | private.example.com signed Billy Bob 6 | registry.access.redhat.com signed security@redhat.com https://cdn.redhat.com/containers/signatures 7 | -------------------------------------------------------------------------------- /tests/unit/test_conf.py: -------------------------------------------------------------------------------- 1 | #pylint: skip-file 2 | import os 3 | import unittest 4 | 5 | import yaml 6 | 7 | 8 | class TestAtomicUnit(unittest.TestCase): 9 | """ 10 | Checks that the configuration file provided is valid. 11 | """ 12 | 13 | def setUp(self): 14 | """ 15 | Provide the conf file contents freshly for each test. 16 | """ 17 | self.conf = open(os.path.sep.join(['atomic.conf']), 'r') 18 | 19 | def tearDown(self): 20 | """ 21 | Close the config file after every test. 22 | """ 23 | self.conf.close() 24 | 25 | def test_config_file_is_valid_yaml(self): 26 | """ 27 | Verifies atomic.conf is valid YAML. 28 | """ 29 | self.assertEquals(type(yaml.safe_load(self.conf)), dict) 30 | 31 | def test_config_file_is_valid_yaml_with_items_uncommented(self): 32 | """ 33 | Verifies atomic.conf is valid YAML when examples are uncommented. 34 | """ 35 | # If a command line has a space after it, it's a comment 36 | # If a comment line has no space after it, it's an example 37 | uncommented = [] 38 | for line in self.conf.readlines(): 39 | if line.startswith('#') and len(line) > 2 and line[1] != ' ': 40 | line = line[1:] 41 | uncommented.append(line) 42 | self.assertEquals(type(yaml.safe_load('\n'.join(uncommented))), dict) 43 | 44 | 45 | if __name__ == '__main__': 46 | unittest.main() 47 | -------------------------------------------------------------------------------- /tests/unit/test_discovery.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from Atomic import util 3 | from Atomic import discovery 4 | 5 | 6 | class TestAtomicUtil(unittest.TestCase): 7 | IMAGE = 'docker.io/library/busybox:latest' 8 | I_REGISTRY, I_REPO, I_IMAGE, I_TAG, _ = util.Decompose(IMAGE).all 9 | 10 | def test_find_image_on_registry(self): 11 | fq = 'docker.io/library/busybox:latest' 12 | for img in ['docker.io/library/busybox:latest', 'docker.io/library/busybox', 'docker.io/busybox', 'busybox']: 13 | registry, repo, image, tag, _ = util.Decompose(img).all 14 | ri = discovery.RegistryInspect(registry=registry, repo=repo, image=image, tag=tag) 15 | self.assertEqual(ri.find_image_on_registry(), fq) 16 | 17 | def test_inspect(self): 18 | ri = discovery.RegistryInspect(registry=self.I_REGISTRY, 19 | repo=self.I_REPO, 20 | image=self.I_IMAGE, 21 | tag=self.I_TAG) 22 | inspect_info = ri.inspect() 23 | self.assertEqual(inspect_info['Name'], "{}/{}/{}".format(self.I_REGISTRY, self.I_REPO, self.I_IMAGE)) 24 | self.assertEqual(inspect_info['Tag'], self.I_TAG) 25 | 26 | if __name__ == '__main__': 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /tests/unit/test_mount.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from Atomic import mount 4 | 5 | 6 | class TestAtomicMount(unittest.TestCase): 7 | def test_mount_excepts_unknown_backend(self): 8 | def mock_info(): 9 | return {'Driver': 'foobardriver'} 10 | with mount.DockerMount('foobar') as m: 11 | m._info = mock_info # pylint: disable=protected-access 12 | exp = 'Atomic mount is not supported on the foobardriver docker ' \ 13 | 'storage backend.' 14 | 15 | # assertRaisesRegexp was deprecated by assertRaisesRegex. 16 | # If it is present, prefer assertRaisesRegex. 17 | if hasattr(self, 'assertRaisesRegex'): 18 | assertRaisesRegex = getattr(self, "assertRaisesRegex") 19 | else: 20 | assertRaisesRegex = getattr(self, "assertRaisesRegexp") 21 | assertRaisesRegex(mount.MountError, exp, m.mount, 'fedora:22') 22 | assertRaisesRegex(mount.MountError, exp, m.unmount) 23 | 24 | def test_dockermount_context_manager(self): 25 | def mock_info(): 26 | return {'Driver': 'foobardriver'} 27 | dm = mount.DockerMount('foobar') 28 | dm._info = mock_info # pylint: disable=protected-access 29 | message = 'Atomic mount is not supported on the foobardriver docker ' \ 30 | 'storage backend.' 31 | with self.assertRaises(mount.MountError) as cm: 32 | with mount.MountContextManager(dm, 'fedora:25'): 33 | pass 34 | self.assertEqual(cm.exception.val, message) 35 | 36 | def test_default_options(self): 37 | with mount.DockerMount('foobar') as m: 38 | o = m.default_options([], default_con='foobar_context', 39 | default_opt=['foo', 'bar']) 40 | self.assertEqual(o, ['foo', 'bar', 'context="foobar_context"']) 41 | 42 | def test_default_options_override_defaults(self): 43 | with mount.DockerMount('foobar') as m: 44 | o = m.default_options(['override', 'opts'], 45 | default_con='foobar_context', 46 | default_opt=['will not appear']) 47 | self.assertEqual(o, ['override', 'opts', 'context="foobar_context"']) 48 | 49 | def test_default_options_no_surplus_context(self): 50 | with mount.DockerMount('foobar') as m: 51 | o = m.default_options(['ro', 'context="foobang_context"'], 52 | default_con='foobar_context') 53 | self.assertEqual(o, ['ro', 'context="foobang_context"']) 54 | 55 | if __name__ == '__main__': 56 | unittest.main() 57 | -------------------------------------------------------------------------------- /tests/unit/test_registries.py: -------------------------------------------------------------------------------- 1 | #pylint: skip-file 2 | 3 | import unittest 4 | from Atomic.util import is_backend_available, load_registries_from_yaml, get_registries 5 | import json 6 | import subprocess 7 | 8 | no_mock = True 9 | try: 10 | from unittest.mock import MagicMock, patch 11 | no_mock = False 12 | except ImportError: 13 | try: 14 | from mock import MagicMock, patch 15 | no_mock = False 16 | except ImportError: 17 | # Mock is already set to False 18 | pass 19 | 20 | 21 | @unittest.skipIf(no_mock, "Mock not found") 22 | class TestRegistriesFromYAML(unittest.TestCase): 23 | 24 | def compare_list_of_dicts(self, results, answer): 25 | if not isinstance(results, list) or not isinstance(answer, list): 26 | raise AssertionError("Results must always be of type list") 27 | if len(results) != len(answer): 28 | raise AssertionError("Length of the lists differ.") 29 | for registry in results: 30 | self.assertDictEqual(registry, next(item for item in answer if item["hostname"] == registry['hostname'])) 31 | 32 | def test_no_registries(self): 33 | with patch('Atomic.util.registries_tool_path') as reg_path: 34 | reg_path.return_value = "/usr/libexec/registries" 35 | with patch('Atomic.util.load_registries_from_yaml') as mockobj: 36 | mockobj.return_value = json.loads("{}") 37 | results = get_registries() 38 | answer = [{'name': 'docker.io', 'hostname': 'registry-1.docker.io', 'search': True, 'secure': True}] 39 | self.compare_list_of_dicts(results, answer) 40 | 41 | def test_block_dockerio(self): 42 | with patch('Atomic.util.registries_tool_path') as reg_path: 43 | reg_path.return_value = "/usr/libexec/registries" 44 | with patch('Atomic.util.load_registries_from_yaml') as mockobj: 45 | mockobj.return_value = json.loads('{"block_registries": ["docker.io"]}') 46 | results = get_registries() 47 | answer = [] 48 | self.compare_list_of_dicts(results, answer) 49 | 50 | 51 | def test_duplicate_dockerio(self): 52 | with patch('Atomic.util.registries_tool_path') as reg_path: 53 | reg_path.return_value = "/usr/libexec/registries" 54 | with patch('Atomic.util.load_registries_from_yaml') as mockobj: 55 | mockobj.return_value = json.loads('{"registries": ["docker.io"]}') 56 | results = get_registries() 57 | answer = [{'secure': True, 'hostname': 'docker.io', 'name': 'docker.io', 'search': True}] 58 | self.compare_list_of_dicts(results, answer) 59 | 60 | def test_all(self): 61 | with patch('Atomic.util.registries_tool_path') as reg_path: 62 | reg_path.return_value = "/usr/libexec/registries" 63 | with patch('Atomic.util.load_registries_from_yaml') as mockobj: 64 | mockobj.return_value = json.loads('{"registries": ["one.com", "two.com"], "insecure_registries": ["three.com"], "block_registries": []}') 65 | results = get_registries() 66 | answer = [{'secure': True, 'search': True, 'name': 'one.com', 'hostname': 'one.com'}, {'secure': True, 'search': True, 'name': 'two.com', 'hostname': 'two.com'}, {'secure': True, 'search': True, 'name': 'three.com', 'hostname': 'three.com'}, {'secure': True, 'search': True, 'name': 'docker.io', 'hostname': 'registry-1.docker.io'}] 67 | self.compare_list_of_dicts(results, answer) 68 | 69 | def test_duplicate_in_secure_and_insecure(self): 70 | with patch('Atomic.util.registries_tool_path') as reg_path: 71 | reg_path.return_value = "/usr/libexec/registries" 72 | with patch('Atomic.util.load_registries_from_yaml') as mockobj: 73 | mockobj.return_value = json.loads('{"registries": ["one.com", "two.com"], "insecure_registries": ["two.com"], "block_registries": []}') 74 | self.assertRaises(ValueError, get_registries) 75 | 76 | def test_duplicate_in_registries(self): 77 | with patch('Atomic.util.registries_tool_path') as reg_path: 78 | reg_path.return_value = "/usr/libexec/registries" 79 | with patch('Atomic.util.load_registries_from_yaml') as mockobj: 80 | mockobj.return_value = json.loads('{"registries": ["one.com", "two.com", "one.com"], "insecure_registries": ["three.com"], "block_registries": []}') 81 | results = get_registries() 82 | answer = [{'secure': True, 'search': True, 'name': 'one.com', 'hostname': 'one.com'}, {'secure': True, 'search': True, 'name': 'two.com', 'hostname': 'two.com'}, {'secure': True, 'search': True, 'name': 'three.com', 'hostname': 'three.com'}, {'secure': True, 'search': True, 'name': 'docker.io', 'hostname': 'registry-1.docker.io'}] 83 | self.compare_list_of_dicts(results, answer) 84 | 85 | if __name__ == '__main__': 86 | unittest.main() 87 | -------------------------------------------------------------------------------- /tests/unit/test_util_sh.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | 4 | from Atomic import util 5 | 6 | class TestAtomicUtilSh(unittest.TestCase): 7 | 8 | def assertShSetEqual(self, a, b): 9 | self.assertEqual(sorted(a.split()), sorted(b.split())) 10 | 11 | def test_util_sh_set(self): 12 | self.assertShSetEqual(util.sh_set_add("foo bar", [ "baz", "bla" ]), 13 | "foo bar baz bla") 14 | self.assertShSetEqual(util.sh_set_add("foo bar", [ "foo" ]), 15 | "foo bar") 16 | self.assertShSetEqual(util.sh_set_del("foo bar", [ "foo" ]), 17 | "bar") 18 | self.assertShSetEqual(util.sh_set_del("foo bar", [ "baz" ]), 19 | "foo bar") 20 | 21 | def test_util_sh_modify_text(self): 22 | def uppercasify(old): 23 | return old.upper() 24 | 25 | # Non-existing setting causes a new entry with default 26 | self.assertEqual(util.sh_modify_var_in_text('', "VAR", uppercasify, "def"), 27 | '\nVAR="DEF"\n') 28 | 29 | # Existing setting will be modified 30 | self.assertEqual(util.sh_modify_var_in_text('VAR="val"\n', "VAR", uppercasify), 31 | 'VAR="VAL"\n') 32 | 33 | # Two settings will both be modified 34 | self.assertEqual(util.sh_modify_var_in_text('VAR="val1"\nVAR="val2"\n', "VAR", uppercasify), 35 | 'VAR="VAL1"\nVAR="VAL2"\n') 36 | 37 | # Setting on partial line is recognized 38 | self.assertEqual(util.sh_modify_var_in_text('VAR="val"', "VAR", uppercasify), 39 | 'VAR="VAL"') 40 | 41 | # Setting with extra whitespace is recognized 42 | self.assertEqual(util.sh_modify_var_in_text(' VAR = "val" \n', "VAR", uppercasify), 43 | 'VAR="VAL"\n') 44 | 45 | # Setting in a comment is not recognized 46 | self.assertEqual(util.sh_modify_var_in_text('# VAR="OLD"\n', "VAR", uppercasify), 47 | '# VAR="OLD"\n\nVAR=""\n') 48 | 49 | # Setting without quotes around the value is not recognized 50 | self.assertEqual(util.sh_modify_var_in_text('VAR=OLD\n', "VAR", uppercasify), 51 | 'VAR=OLD\n\nVAR=""\n') 52 | 53 | def assertFileEqual(self, path, content): 54 | with open(path, "r") as f: 55 | data = f.read() 56 | self.assertEqual(data, content) 57 | 58 | def test_util_sh_modify_file(self): 59 | path = os.path.join(os.environ["WORK_DIR"], "sh.conf") 60 | 61 | def uppercasify(old): 62 | return old.upper() 63 | 64 | # Non-existing file is treated as empty 65 | self.assertFalse(os.path.exists(path)) 66 | util.sh_modify_var_in_file(path, "VAR", uppercasify, "def") 67 | self.assertFileEqual(path, '\nVAR="DEF"\n') 68 | 69 | # Existing file is modified in place as expected 70 | with open(path, "w") as f: 71 | f.write('VAR="val"\n') 72 | util.sh_modify_var_in_file(path, "VAR", uppercasify) 73 | self.assertFileEqual(path, 'VAR="VAL"\n') 74 | 75 | if __name__ == '__main__': 76 | unittest.main() 77 | -------------------------------------------------------------------------------- /tests/unit_template.py: -------------------------------------------------------------------------------- 1 | # 2 | # Project Atomic Unit Test Example Template 3 | # 4 | #pylint: skip-file 5 | import unittest 6 | 7 | 8 | class TestAtomicUnit(unittest.TestCase): 9 | """ 10 | TODO: This test case ... 11 | """ 12 | 13 | def test_unit_desired_behavior(self): 14 | """ 15 | TODO: Verifies ... 16 | """ 17 | self.assertTrue(True) 18 | 19 | 20 | if __name__ == '__main__': 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /vagrant.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | _FINISH(){ 5 | RESULT=($?) 6 | if [ ${RESULT} -eq 0 ]; then 7 | echo "" 8 | echo "Tests completed normally..." 9 | vagrant destroy ${BOX} 10 | echo "" 11 | else 12 | echo "" 13 | echo "** Test failed. Leaving '${BOX}' running for debug." 2>&1 | tee -a ${tee_file} 14 | echo "** Be sure to halt or destroy prior to re-running the check" 2>&1 | tee -a ${tee_file} 15 | echo "** Logs are stored at ${tee_file}" 16 | echo "" 17 | exit 1 18 | fi 19 | } 20 | 21 | # When make calls bash, the real signals are not surfaced 22 | # correctly to trap. So we trap on EXIT and then sort it 23 | # out in _FINISH 24 | trap _FINISH EXIT 25 | 26 | BOXES="fedora_atomic centos_atomic fedora_cloud" 27 | 28 | is_running() { 29 | status=$(vagrant status | grep ${BOX} | awk '{print $2}') 30 | if [ ${status} == "running" ]; then 31 | RUNNING=true 32 | else 33 | RUNNING=false 34 | 35 | fi 36 | } 37 | 38 | 39 | if [[ ! $BOXES =~ $BOX ]]; then 40 | echo "" 41 | echo "Invalid BOX name: $BOX. Valid choices are $BOXES" 42 | echo "" 43 | exit 1 44 | fi 45 | 46 | echo "Testing on ${BOX}" 47 | timestamp=$(date +%Y_%m_%d_%H_%M) 48 | tee_file="${BOX}_${timestamp}.log" 49 | is_running 50 | 51 | if ${RUNNING}; then 52 | echo "" 53 | echo "*** '${BOX}' is already running. Re-syncing and rerunning test ..." 54 | echo "" 55 | vagrant rsync ${BOX} 56 | else 57 | vagrant up ${BOX} 2>&1 | tee ${tee_file} 58 | fi 59 | 60 | vagrant ssh ${BOX} -c "cd /home/vagrant/atomic && sudo sh ./.papr.sh" 2>&1 | tee -a ${tee_file} 61 | --------------------------------------------------------------------------------