Commit 1b245ace authored by David M. Rogers's avatar David M. Rogers
Browse files

Initial project.

parents
Loading
Loading
Loading
Loading

.gitignore

0 → 100644
+2 −0
Original line number Diff line number Diff line
__pycache__
*.pyc

README.md

0 → 100644
+50 −0
Original line number Diff line number Diff line
Contaminate
===========

This package allows podman operations to be
scripted from python.
Using it, we can create packages in slackware
style.

For example, installing build-essentials on a
system with dpkg,

    from contaminate import Script

    build_essentials = Script("""
    ENV DEBIAN_FRONTEND=noninteractive

    # Install package dependencies
    RUN apt-get update && \
        apt-get install -y --no-install-recommends software-properties-common build-essential \
                autoconf libtool pkg-config ca-certificates libssl-dev pkg-config \
                git wget vim cppman \
            automake g++ gcc \
            gdb valgrind && \
    apt-get clean
    """)

This is an Action that can be run to make
a new container image from an old one.

To instantiate it, create a container environment,
then a base container with that environment.
Then apply the action.

    from contaminate import Container, ContainerEnv, apply

    env = ContainerEnv(system="localhost",
                       arch="linux/x86_64",
                       bind=["/home"])
    c = Container(name="ubuntu:16.04", env=env, value="")
    c2 = apply(c, build_essentials)

    print(c2.name)

You can also run commands directly in the container
using the Shell action, and copy files from
the container using the Download action,

    apply(c2, Shell(script="w"))
    apply(c2, Download(src="/etc/hosts", dst="hostfile"))
+3 −0
Original line number Diff line number Diff line
from .container import Container
from .container_env import ContainerEnv
from .action import Return, Script, Shell, Upload, Download, applyM

contaminate/action.py

0 → 100644
+67 −0
Original line number Diff line number Diff line
from enum import Enum
from typing import Literal, Union, Callable
from pathlib import Path

from pydantic import BaseModel, Field

from .container import Container
from .container_env import ContainerEnv
from .podman import runcmd

class Return(BaseModel):
    action_type : Literal["return"]
    value       : str

    def __call__(self, c : Container) -> Container:
        return c.with_value(self.value)

class Script(BaseModel):
    action_type : Literal["script"]
    script      : str

    def __call__(self, c : Container) -> Container:
        return c.run_cmd(self.script)

class Shell(BaseModel):
    action_type : Literal["shell"]
    script      : str

    def __call__(self, c : Container) -> Container:
        return c.run_shell(self.script)

class Upload(BaseModel):
    action_type : Literal["upload"]
    src         : Path # local
    dst         : Path # remote
    extract     : bool = False # use ADD instead of COPY

    def __call__(self, c : Container) -> Container:
        cmd = f"COPY {self.src} {self.dst}"
        if self.extract:
            cmd = f"ADD {self.src} {self.dst}"
        return c.run_cmd(cmd)

class Download(BaseModel):
    action_type : Literal["download"]
    src         : Path # remote
    dst         : Path # local

    def __call__(self, c : Container) -> Container:
        runcmd("podman", "cp",
               f"{c.name}:{self.src}", self.dst)
        return c

Actions = Union[Return, Script,
                  Shell, Upload, Download]
class Action(BaseModel):
    action: Actions = Field(...,
                            discriminator="action_type")
    def __call__(self, c : Container) -> Container:
        return self.action(c)

def apply(c : Container, action : Action) -> Container:
    return action(c)

def applyM(c : Container, k : Callable[[str], Action]) -> Container:
    action = k(c.value)
    return action(c)
+43 −0
Original line number Diff line number Diff line
from pathlib import Path

from pydantic import BaseModel

from .container_env import ContainerEnv
import contaminate.podman as podman

def incr_name(name : str) -> str:
    s = name.rsplit(".", 1)
    if len(s) == 1:
        return f"{name}.1"
    try:
        i = int(s[1])
    except ValueError:
        i = 0
    return f"{name}.{i+1}"

class Container(BaseModel):
    name   : str           # container name
    #path   : Path          # file path to container
    env    : ContainerEnv  # run-environment needed to use container
    value  : str           # current container output text

    def with_value(self, val : str) -> "Container":
        return Container(name=self.name, #path=self.path,
                         env=self.env, value=val)

    def with_env(self, env : ContainerEnv) -> "Container":
        return Container(name=self.name, #path=self.path,
                         env=env, value=self.value)

    def run_cmd(self, cmd : str) -> "Container":
        new_name = incr_name(self.name)
        ans = podman.build(self.name, new_name, cmd)
        return Container(name=new_name,
                         #path=find_path(new_name),
                         env=self.env,
                         value=ans
                        )

    def run_shell(self, cmds : str) -> "Container":
        ans = podman.run(self.name, cmds)
        return self.with_value(ans)