<commit_before>/*
 * Copyright 1&1 Internet AG, https://github.com/1and1/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.oneandone.stool.kubernetes;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import io.kubernetes.client.custom.Quantity;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1ContainerState;
import io.kubernetes.client.openapi.models.V1ContainerStateRunning;
import io.kubernetes.client.openapi.models.V1ContainerStatus;
import io.kubernetes.client.openapi.models.V1EnvVar;
import io.kubernetes.client.openapi.models.V1HostPathVolumeSource;
import io.kubernetes.client.openapi.models.V1Namespace;
import io.kubernetes.client.openapi.models.V1NamespaceBuilder;
import io.kubernetes.client.openapi.models.V1NamespaceList;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodBuilder;
import io.kubernetes.client.openapi.models.V1PodList;
import io.kubernetes.client.openapi.models.V1Secret;
import io.kubernetes.client.openapi.models.V1SecretBuilder;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServiceBuilder;
import io.kubernetes.client.openapi.models.V1ServiceList;
import io.kubernetes.client.openapi.models.V1ServicePort;
import io.kubernetes.client.openapi.models.V1Volume;
import io.kubernetes.client.openapi.models.V1VolumeMount;
import io.kubernetes.client.util.Config;
import net.oneandone.stool.docker.Daemon;
import net.oneandone.stool.docker.ImageInfo;
import net.oneandone.stool.docker.Registry;
import net.oneandone.stool.server.ArgumentException;
import net.oneandone.sushi.fs.FileNotFoundException;
import net.oneandone.sushi.fs.World;
import net.oneandone.sushi.fs.file.FileNode;
import net.oneandone.sushi.fs.http.HttpNode;
import net.oneandone.sushi.util.Strings;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Connect to local docker engine via unix socket. https://docs.docker.com/engine/api/v1.37/
 * Not thread-safe because the io buffer is shared.
 */
public class Engine implements AutoCloseable {
    private static final String UTF_8 = "utf8";

    public static String encodeLabel(String value) {
        try {
            return "a-" + Base64.getEncoder().encodeToString(value.getBytes(UTF_8)).replace('=', '-') + "-z";
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }

    public static String decodeLabel(String value) {
        value = Strings.removeLeft(value, "a-");
        value = Strings.removeRight(value, "-z");
        value = value.replace('-', '=');
        try {
            return new String(Base64.getDecoder().decode(value), UTF_8);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }

    //--

    public static final DateTimeFormatter CREATED_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.n'Z'");

    public static Engine create() throws IOException {
        return create(null);
    }

    public static Engine create(String wirelog) throws IOException {
        return create("/var/run/docker.sock", wirelog);
    }

    public static Engine create(String socketPath, String wirelog) throws IOException {
        Engine result;
        PodInfo r;
        HttpNode root;
        JsonObject i;

        result = new Engine(Daemon.create(socketPath, wirelog), null);
        r = result.podProbe("stool-registry");
        // TODO
        if (r != null) {
            System.out.println("registry " + r.ip);
            root = (HttpNode) World.create().validNode("http://" + r.ip + ":5000");
            result.registry = Registry.create(root, null);
        }
        return result;
    }

    private final ApiClient client;
    private final CoreV1Api core;
    private final String namespace;
    public final Daemon docker;
    private Registry registry;

    private Engine(Daemon docker, Registry registry) throws IOException {
        this.docker = docker;
        this.registry = registry;

        client = Config.defaultClient();
        Configuration.setDefaultApiClient(client); // TODO: threading ...

        // client.setDebugging(true);
        core = new CoreV1Api();
        namespace = "stool";
    }

    public void close() {
        // TODO: https://github.com/kubernetes-client/java/issues/865
        client.getHttpClient().connectionPool().evictAll();
        docker.close();
    }


    //--

    // TODO: kubernetes version
    public String version() throws IOException {
        return docker.version();
    }

    //-- images

    /** @return image ids mapped to ImageInfo */
    public Map<String, net.oneandone.stool.docker.ImageInfo> imageList() throws IOException {
        return imageList(Collections.emptyMap());
    }

    // TODO: performance, caching
    public Map<String, ImageInfo> imageList(Map<String, String> labels) throws IOException {
        ImageInfo info;
        Map<String, ImageInfo> result;

        result = new HashMap<>();
        for (String repository : registry.catalog()) {
            for (String tag : registry.tags(repository)) {<commit_msg>get rid of docker api<commit_after>/*
 * Copyright 1&1 Internet AG, https://github.com/1and1/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.oneandone.stool.kubernetes;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import io.kubernetes.client.custom.Quantity;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1ContainerState;
import io.kubernetes.client.openapi.models.V1ContainerStateRunning;
import io.kubernetes.client.openapi.models.V1ContainerStatus;
import io.kubernetes.client.openapi.models.V1EnvVar;
import io.kubernetes.client.openapi.models.V1HostPathVolumeSource;
import io.kubernetes.client.openapi.models.V1Namespace;
import io.kubernetes.client.openapi.models.V1NamespaceBuilder;
import io.kubernetes.client.openapi.models.V1NamespaceList;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodBuilder;
import io.kubernetes.client.openapi.models.V1PodList;
import io.kubernetes.client.openapi.models.V1Secret;
import io.kubernetes.client.openapi.models.V1SecretBuilder;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServiceBuilder;
import io.kubernetes.client.openapi.models.V1ServiceList;
import io.kubernetes.client.openapi.models.V1ServicePort;
import io.kubernetes.client.openapi.models.V1Volume;
import io.kubernetes.client.openapi.models.V1VolumeMount;
import io.kubernetes.client.util.Config;
import net.oneandone.stool.docker.Daemon;
import net.oneandone.stool.docker.ImageInfo;
import net.oneandone.stool.docker.Registry;
import net.oneandone.stool.server.ArgumentException;
import net.oneandone.sushi.fs.FileNotFoundException;
import net.oneandone.sushi.fs.World;
import net.oneandone.sushi.fs.file.FileNode;
import net.oneandone.sushi.fs.http.HttpNode;
import net.oneandone.sushi.util.Strings;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Connect to local docker engine via unix socket. https://docs.docker.com/engine/api/v1.37/
 * Not thread-safe because the io buffer is shared.
 */
public class Engine implements AutoCloseable {
    private static final String UTF_8 = "utf8";

    public static String encodeLabel(String value) {
        try {
            return "a-" + Base64.getEncoder().encodeToString(value.getBytes(UTF_8)).replace('=', '-') + "-z";
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }

    public static String decodeLabel(String value) {
        value = Strings.removeLeft(value, "a-");
        value = Strings.removeRight(value, "-z");
        value = value.replace('-', '=');
        try {
            return new String(Base64.getDecoder().decode(value), UTF_8);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }

    //--

    public static final DateTimeFormatter CREATED_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.n'Z'");

    public static Engine create() throws IOException {
        return create(null);
    }

    public static Engine create(String wirelog) throws IOException {
        return create("/var/run/docker.sock", wirelog);
    }

    public static Engine create(String socketPath, String wirelog) throws IOException {
        Engine result;
        PodInfo r;
        HttpNode root;
        JsonObject i;

        result = new Engine(Daemon.create(socketPath, wirelog), null);
        r = result.podProbe("stool-registry");
        // TODO
        if (r != null) {
            System.out.println("registry " + r.ip);
            root = (HttpNode) World.create().validNode("http://" + r.ip + ":5000");
            result.registry = Registry.create(root, null);
        }
        return result;
    }

    private final ApiClient client;
    private final CoreV1Api core;
    private final String namespace;
    public final Daemon docker;
    private Registry registry;

    private Engine(Daemon docker, Registry registry) throws IOException {
        this.docker = docker;
        this.registry = registry;

        client = Config.defaultClient();
        Configuration.setDefaultApiClient(client); // TODO: threading ...

        // client.setDebugging(true);
        core = new CoreV1Api();
        namespace = "stool";
    }

    public void close() {
        // TODO: https://github.com/kubernetes-client/java/issues/865
        client.getHttpClient().connectionPool().evictAll();
        docker.close();
    }


    //--

    public String version() throws IOException {
        return "TODO";
    }

    //-- images

    /** @return image ids mapped to ImageInfo */
    public Map<String, net.oneandone.stool.docker.ImageInfo> imageList() throws IOException {
        return imageList(Collections.emptyMap());
    }

    // TODO: performance, caching
    public Map<String, ImageInfo> imageList(Map<String, String> labels) throws IOException {
        ImageInfo info;
        Map<String, ImageInfo> result;

        result = new HashMap<>();
        for (String repository : registry.catalog()) {
            for (String tag : registry.tags(repository)) {
{'fraction_non_alphanumeric': 0.10956144428947777, 'fraction_numerical': 0.008368200836820083, 'mean_word_length': 3.573706591070163, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5011454', 'n_tokens_mistral': 3817, 'n_tokens_neox': 3652, 'n_words': 849}