All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.moe.gradle.remote.ServerSettings Maven / Gradle / Ivy

/*
Copyright (C) 2016 Migeran

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 org.moe.gradle.remote;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.moe.common.utils.CloseableUtil;
import org.moe.gradle.MoePlugin;
import org.moe.gradle.anns.NotNull;
import org.moe.gradle.anns.Nullable;
import org.moe.gradle.utils.FileUtils;
import org.moe.gradle.utils.Require;
import org.moe.gradle.utils.TermColor;

import java.io.ByteArrayOutputStream;
import java.io.Console;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import static org.moe.gradle.utils.TermColor.*;

class ServerSettings {

    private static final Logger LOG = Logging.getLogger(ServerSettings.class);

    private static final String MOE_REMOTEBUILD_PROPERTIES = "moe.remotebuild.properties";
    private static final String MOE_REMOTEBUILD_PROPERTIES_IGNORE_PROPERTY = "moe.remotebuild.properties.ignore";

    private interface Validator {
        @Nullable
        T validate(@NotNull MoePlugin plugin, @Nullable String value) throws IOException;
    }

    private static class Key {
        @NotNull
        final String key;
        @NotNull
        final String property;
        @NotNull
        final String description;
        @NotNull
        final Validator validator;

        private Key(@NotNull String key, @NotNull String description, @NotNull Validator validator) {
            this.key = Require.nonNull(key);
            this.property = "moe.remotebuild." + key;
            this.description = Require.nonNull(description);
            this.validator = Require.nonNull(validator);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Key that = (Key) o;
            return Objects.equals(key, that.key);
        }

        @Override
        public int hashCode() {
            return Objects.hash(key);
        }
    }

    private static final Key HOST_KEY = new Key<>("host", "address of the remote build server", (plugin, value) -> {
        if (value == null) {
            return null;
        }
        InetAddress address;
        try {
            address = InetAddress.getByName(value);
        } catch (UnknownHostException ex) {
            throw new IOException("illegal host '" + value + "'");
        }
        try {
            boolean reachable = address.isReachable(5000);
            if (!reachable) {
                throw new IOException();
            }
        } catch (IOException ex) {
            printWarning("host '" + value + "' is unreachable");
        }
        return value;
    });

    private static final Key PORT_KEY = new Key<>("port", "port for ssh, defaults to 22", (plugin, value) -> {
        if (value == null) {
            return null;
        }

        int i = getInteger(value);
        if (i < 0 || i > 65535) {
            throw new IOException("'" + value + "' is not a number between [0..65535]");
        }
        return i;
    });

    private static final Key USER_KEY = new Key<>("user", "user on the remote build server", (plugin, value) -> {
        if (value == null) {
            return null;
        } else {
            return value;
        }
    });

    private static final Key KNOWNHOSTS_KEY = new Key<>("knownhosts", "path to known_hosts file", (plugin, value) -> {
        if (value == null) {
            return null;
        }

        File file = plugin.getProject().file(getFileWithProperty(plugin.getProject(), value));
        if (!file.isFile()) {
            printWarning("'" + value + "' doesn't exist or is not a file");
        }
        return value;
    });

    private static final Key IDENTITY_KEY = new Key<>("identity", "path to private key", (plugin, value) -> {
        if (value == null) {
            return null;
        }

        File file = plugin.getProject().file(getFileWithProperty(plugin.getProject(), value));
        if (!file.isFile()) {
            printWarning("'" + value + "' doesn't exist or is not a file");
        }
        return value;
    });

    private static final Key KEYCHAIN_NAME_KEY = new Key<>("keychain.name", "name of keychain to unlock, defaults to 'moeremotebuild.keychain'", (plugin, value) -> {
        if (value == null) {
            return null;
        }
        return value;
    });

    private static final Key KEYCHAIN_PASS_KEY = new Key<>("keychain.pass", "password for keychain, defaults to ''", (plugin, value) -> {
        if (value == null) {
            return null;
        }
        return value;
    });

    private static final Key KEYCHAIN_LOCKTIMEOUT_KEY = new Key<>("keychain.locktimeout", "keychain lock timeout in seconds, defaults to 3600", (plugin, value) -> {
        if (value == null) {
            return null;
        }

        int i;
        try {
            i = Integer.parseInt(value);
        } catch (NumberFormatException ex) {
            throw new IOException("'" + value + "' is not a number grater than 0");
        }
        if (i < 0) {
            throw new IOException("'" + value + "' is not a number grater than 0");
        }
        return i;
    });

    private static final Key GRADLE_REPOSITORIES_KEY = new Key<>("gradle.repositories", "repositories to be used when setting up the MOE SDK on the remote server, defaults to 'mavenCentral()'", (plugin, value) -> {
        if (value == null) {
            return null;
        }
        return value;
    });

    private static final Key[] ALL_KEYS = new Key[]{HOST_KEY, PORT_KEY, USER_KEY, KNOWNHOSTS_KEY,
            IDENTITY_KEY, KEYCHAIN_NAME_KEY, KEYCHAIN_PASS_KEY, KEYCHAIN_LOCKTIMEOUT_KEY, GRADLE_REPOSITORIES_KEY};

    @NotNull
    private final Map settings = new HashMap<>();

    @Nullable
    private Properties properties;

    @NotNull
    private final MoePlugin plugin;

    ServerSettings(@NotNull MoePlugin plugin) {
        this.plugin = Require.nonNull(plugin);

        fillUnset();
        load();
    }

    private void fillUnset() {
        for (Key key : ALL_KEYS) {
            try {
                settings.put(key, key.validator.validate(plugin, null));
            } catch (IOException ignore) {
            }
        }
    }

    private void load() {
        // Load properties file if exists
        final File propsFile = plugin.getProject().file(MOE_REMOTEBUILD_PROPERTIES);

        properties = new Properties();
        try {
            if (propsFile.exists()) {
                properties.load(new FileInputStream(propsFile));
            }
        } catch (IOException e) {
            throw new GradleException(e.getMessage(), e);
        }

        for (Key key : ALL_KEYS) {
            if (plugin.getProject().hasProperty(key.property)) {
                try {
                    settings.put(key, key.validator.validate(plugin, plugin.getProject().property(key.property).toString()));
                } catch (IOException e) {
                    LOG.warn("Failed to load '" + key.key + "' from project property: " + e.getMessage());
                }
                continue;
            }
            if (!plugin.getProject().hasProperty(MOE_REMOTEBUILD_PROPERTIES_IGNORE_PROPERTY)) {
                try {
                    settings.put(key, key.validator.validate(plugin, properties.getProperty(key.key)));
                } catch (IOException e) {
                    LOG.warn("Failed to load '" + key.key + "' from " + MOE_REMOTEBUILD_PROPERTIES + ": " + e.getMessage());
                }
            }
        }
    }

    private void store() {
        if (properties == null) {
            properties = new Properties();
        }

        final StringBuilder usage = new StringBuilder();
        usage.append(" Supported keys and values:\n");
        for (Key key : ALL_KEYS) {
            usage.append("   ").append(key.key).append(": ").append(key.description).append("\n");
        }
        usage.append("\n" +
                " The identity and knownhosts keys accept special parameters to access\n" +
                "   environmental variables ($env$KEY), system properties ($sys$KEY)\n" +
                "   and project properties ($proj$KEY).\n")
                .append("   Example: knownhosts=$sys$user.home/.ssh/known_hosts\n");

        // Save properties
        final File propsFile = plugin.getProject().file(MOE_REMOTEBUILD_PROPERTIES);
        for (Key key : ALL_KEYS) {
            // Do not store values which were specified on the commandline
            if (plugin.getProject().hasProperty(key.property)) {
                continue;
            }

            Object value = settings.get(key);
            if (value != null) {
                properties.put(key.key, value.toString());
            } else {
                properties.remove(key.key);
            }
        }
        try {
            properties.store(new FileOutputStream(propsFile), usage.toString());
        } catch (IOException e) {
            throw new GradleException(e.getMessage(), e);
        }
    }

    public boolean isConfigured() {
        return get(HOST_KEY) != null;
    }

    @NotNull
    public String getKeychainName() {
        final String value = get(KEYCHAIN_NAME_KEY);
        return value == null ? "moeremotebuild.keychain" : value;
    }

    @NotNull
    public String getKeychainPass() {
        final String value = get(KEYCHAIN_PASS_KEY);
        return value == null ? "" : value;
    }

    @NotNull
    public int getKeychainLockTimeout() {
        final Integer value = get(KEYCHAIN_LOCKTIMEOUT_KEY);
        return value == null ? 3600 : value;
    }

    @NotNull
    public String getGradleRepositories() {
        final String value = get(GRADLE_REPOSITORIES_KEY);
        return value == null ? "mavenCentral()" : value;
    }

    private static class OptionScreen {

        private Runnable beforeLoop;

        private interface Action {
            boolean run();
        }

        private static class Option {
            @NotNull
            private final String opt;

            @NotNull
            private final String title;

            @Nullable
            private final String help;

            @NotNull
            private final TermColor color;

            @NotNull
            private final Action action;

            public Option(@NotNull String opt, @NotNull String title, @Nullable String help, @NotNull TermColor color,
                          @NotNull Action action) {
                this.opt = Require.nonNull(opt);
                this.title = Require.nonNull(title);
                this.help = help;
                this.color = Require.nonNull(color);
                this.action = Require.nonNull(action);
            }
        }

        @NotNull
        private final String title;

        @NotNull
        private final List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy