Commit 56a4fb5a authored by jan.koester's avatar jan.koester
Browse files

test

parent b6af1cad
Loading
Loading
Loading
Loading
+3 −0
Changes for include/containerd/api.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -137,6 +137,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *   EditContainer {name, settings}   rename: new_name (stopped only)
 *   DeleteContainer {name}           stopped only; its changes go too
 *   StartContainer {name} -> state (running, or failed and error)
 *       with network = host not (409) while a port its image exposes is
 *       exposed by another running container with the host's network or
 *       in use on the host (a listening TCP or bound UDP socket)
 *   StopContainer {name, timeout? (s, default 10)}  the image's
 *       StopSignal (default SIGTERM) to the container, after timeout
 *       everything in its cgroup is killed; replies at once (stopping)
+72 −0
Changes for src/containers.cpp: 72 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -30,10 +30,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <cerrno>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <sstream>

#include <fcntl.h>
#include <poll.h>
@@ -291,6 +293,74 @@ void readStatus(const std::string &name, Runtime &r) {
    }
}

// an image's exposed port ("80/tcp", "53/udp", "8000-8010/tcp", "80"):
// its protocol and range; false if it doesn't read as one
bool exposedPort(const std::string &spec, std::string &proto, int &first, int &last) {
    size_t slash = spec.find('/');
    proto = slash == std::string::npos ? "tcp" : spec.substr(slash + 1);
    std::string range = spec.substr(0, slash);
    size_t dash = range.find('-');
    first = atoi(range.substr(0, dash).c_str());
    last = dash == std::string::npos ? first : atoi(range.substr(dash + 1).c_str());
    return (proto == "tcp" || proto == "udp") && first > 0 && first <= last && last < 65536;
}

// the host's ports of a protocol something listens on (tcp) or is bound
// to (udp), on any address: a bind to 0.0.0.0 fails on any of them
std::vector<int> hostPorts(const std::string &proto) {
    std::vector<int> out;
    for (const char *v : {"", "6"}) {
        std::string text;
        if (!readFile("/proc/net/" + proto + v, text)) continue;
        std::istringstream lines(text);
        std::string line;
        std::getline(lines, line);  // the header
        while (std::getline(lines, line)) {
            std::istringstream f(line);
            std::string sl, local, remote, st;
            if (!(f >> sl >> local >> remote >> st)) continue;
            // 0A: TCP_LISTEN; 07: TCP_CLOSE, an unconnected UDP socket
            if (st != (proto == "tcp" ? "0A" : "07")) continue;
            size_t colon = local.rfind(':');
            if (colon != std::string::npos) out.push_back((int)strtol(local.c_str() + colon + 1, nullptr, 16));
        }
    }
    return out;
}

// with the host's network a container's ports are the host's: what its
// image exposes mustn't be taken by another container with the host's
// network or by something on the host ("" if it is free)
std::string portConflict(const std::string &name, const Settings &s, const Image &image) {
    if (s.network != "host") return "";
    for (auto &spec : image.ports) {
        std::string proto;
        int first, last;
        if (!exposedPort(spec, proto, first, last)) continue;
        std::string what = spec.substr(0, spec.find('/')) + "/" + proto;
        auto overlaps = [&](const std::string &other) {
            std::string p;
            int a, b;
            return exposedPort(other, p, a, b) && p == proto && a <= last && first <= b;
        };
        for (auto &[other, r] : table()) {
            Settings os;
            Image oi;
            std::string err;
            if (other == name || !active(r) || !load(other, os, err) || os.network != "host" || !loadImage(os.image, oi)) continue;
            if (std::any_of(oi.ports.begin(), oi.ports.end(), overlaps))
                return tr("port %1 is taken by the container %2 (both have the host's network; give one network=bridge)",
                          "Port %1 ist vom Container %2 belegt (beide haben das Netz des Hosts; einem network=bridge geben)", {what, other});
        }
        for (int port : hostPorts(proto))
            if (port >= first && port <= last)
                return tr("port %1 is in use on the host (the container has the host's network; give it network=bridge)",
                          "Port %1 ist auf dem Host belegt (der Container hat das Netz des Hosts; ihm network=bridge geben)",
                          {std::to_string(port) + "/" + proto});
    }
    return "";
}

Result startOne(const std::string &name) {
    Settings s;
    std::string err;
@@ -310,6 +380,8 @@ Result startOne(const std::string &name) {
        if (!isDir(layerDir(layer))) return fail(tr("a layer of %1 is missing; pull it again", "eine Schicht von %1 fehlt; bitte neu laden", {s.image}), 500);
    if (s.network == "bridge" && !isDir("/sys/class/net/" + s.bridge + "/bridge"))
        return fail(tr("there is no bridge %1", "es gibt keine Bridge %1", {s.bridge}), 409);
    err = portConflict(name, s, l.image);
    if (!err.empty()) return fail(err, 409);
    // a missing bind mount source is created as a directory (a file to
    // mount has to exist)
    for (auto &b : s.binds) {