package be.iminds.ilabt.jfed.util;

import be.iminds.ilabt.jfed.lowlevel.GeniUser;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnection;
import be.iminds.ilabt.jfed.rspec.model.BasicStringRspec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * AnsibleFileWriter
 */
public class AnsibleFileWriter {
    private static final Logger LOG = LoggerFactory.getLogger(AnsibleFileWriter.class);

    public enum PrivateKeyHandling {
        COPY,
        LINK,
        /**
         * this prevents any copying or link of private keys. This is secure, but only works is an ssh-agent is running.
         */
        DONTUSE
    }

    private final BasicStringRspec basicStringRspec;
    private final List<BasicStringRspec> altBasicStringRspecs;
    private final PrivateKeyHandling privateKeyHandling;
    private final File privateKeyFile;
    private final PrivateKey privateKey;
    private final PublicKey publicKey;
    private final GeniUser loggedInUser;
    private final String preferredUser;
    /*
     * If not null, and there is no testbed proxy for a node, this will be set as proxy for the node.
     * This is used, for example, in the GUI to set the activated proxy.
     **/
    private final JFedConnection.SshProxyInfo proxyInfo;

    private AnsibleFileWriter(BasicStringRspec basicStringRspec,
                              PrivateKeyHandling privateKeyHandling,
                              File privateKeyFile,
                              PrivateKey privateKey,
                              PublicKey publicKey,
                              GeniUser loggedInUser,
                              JFedConnection.SshProxyInfo proxyInfo,
                              String preferredUser) {
        this.basicStringRspec = basicStringRspec;
        this.altBasicStringRspecs = new ArrayList<>();
        this.privateKeyHandling = privateKeyHandling;
        this.privateKeyFile = privateKeyHandling == PrivateKeyHandling.DONTUSE ? null
                : privateKeyFile; //make sure it can't get used by accident
        this.privateKey = privateKeyHandling == PrivateKeyHandling.DONTUSE ? null
                : privateKey; //make sure it can't get used by accident
        this.publicKey = publicKey;
        this.loggedInUser = loggedInUser;
        this.preferredUser = preferredUser;
        this.proxyInfo = proxyInfo;

        if (privateKeyHandling == PrivateKeyHandling.LINK)
            assert privateKeyFile != null;
        if (privateKeyHandling == PrivateKeyHandling.COPY)
            assert privateKey != null;
    }

    public static AnsibleFileWriter createWithoutPrivateKey(BasicStringRspec basicStringRspec, File privateKeyFile, PublicKey publicKey, GeniUser loggedInUser, JFedConnection.SshProxyInfo proxyInfo, String preferredUser) {
        return new AnsibleFileWriter(basicStringRspec, PrivateKeyHandling.DONTUSE, privateKeyFile, null, publicKey, loggedInUser, proxyInfo, preferredUser);
    }

    public static AnsibleFileWriter createWithLinkedPrivateKey(BasicStringRspec basicStringRspec, File privateKeyFile, PublicKey publicKey, GeniUser loggedInUser, JFedConnection.SshProxyInfo proxyInfo, String preferredUser) {
        return new AnsibleFileWriter(basicStringRspec, PrivateKeyHandling.LINK, privateKeyFile, null, publicKey, loggedInUser, proxyInfo, preferredUser);
    }

    public static AnsibleFileWriter createWithCopiedPrivateKey(BasicStringRspec basicStringRspec, PrivateKey privateKey, PublicKey publicKey, GeniUser loggedInUser, JFedConnection.SshProxyInfo proxyInfo, String preferredUser) {
        return new AnsibleFileWriter(basicStringRspec, PrivateKeyHandling.COPY, null, privateKey, publicKey, loggedInUser, proxyInfo, preferredUser);
    }

    /**
     * add an alternate manifest RSpec.
     * This will only be used when no suitable login is found in the primary manifest RSpec.
     * This is useful if there are multiple manifests available. (= special case in automated tester)
     * <p>
     * It is also possible to use this with all individual manifests for each AM, instead of creating one merged manifest.
     * <p>
     * IMPORTANT: it is assumed that all nodes are present in basicStringRspec (with or without login info),
     * which is thus the primary manifest or the request RSpec.
     * alt manifest RSpec are however allowed to miss certain nodes.
     *
     * @param altBasicStringRspec alternative manifest rspec
     */
    public void addAltBasicStringRspec(BasicStringRspec altBasicStringRspec) {
        altBasicStringRspecs.add(altBasicStringRspec);
    }

    public void addAltBasicStringRspec(String altRspecString) {
        altBasicStringRspecs.add(new BasicStringRspec(altRspecString));
    }

    public static final String PUBLIC_KEY_FILE_BASENAME = "id_rsa.pub";
    public static final String PRIVATE_KEY_FILE_BASENAME = "id_rsa";
    public static final String PROXY_PRIVATE_KEY_FILE_TEMPLATE = "id_rsa_proxy_";
    public static final String ANSIBLE_HOSTS_FILE_BASENAME = "ansible-hosts";
    public static final String ANSIBLE_CONFIG_FILE_BASENAME = "ansible.cfg";
    public static final String FABRIC_FILE_BASENAME = "fabfile.py";
    public static final String SSH_CONFIG_FILE_BASENAME = "ssh-config";
    public static final String SSH_PROXY_KNOWN_HOST_TEMPLATE = "known_host_proxy_";
    public static final String README_FILE_BASENAME = "README.txt";
    public static final String REQUIREMENTS_FILE_BASENAME = "requirements.txt";

    public String getPublicKeyFileContent() {
        return publicKey == null ? null : KeyUtil.publicKeyToOpenSshAuthorizedKeysFormat(publicKey);
    }

    public String getAnsibleConfigFileContent() {
        String privateKeyFilePath;
        switch (privateKeyHandling) {
            case COPY: {
                privateKeyFilePath = "./" + PRIVATE_KEY_FILE_BASENAME + "";
                break;
            }
            case LINK: {
                privateKeyFilePath = privateKeyFile.getAbsolutePath();
                break;
            }
            case DONTUSE: {
                privateKeyFilePath = null;
                break;
            }
            default:
                throw new RuntimeException("Unsupported privateKeyHandling -> " + privateKeyHandling);
        }

        return "[defaults]\n" +
                (privateKeyFilePath == null ? "" : "private_key_file = " + privateKeyFilePath + "\n") +
                "host_key_checking = false\n" +
                "inventory = " + ANSIBLE_HOSTS_FILE_BASENAME + "\n" +
                "\n" +
                "[ssh_connection]\n" +
                "ssh_args = -F " + SSH_CONFIG_FILE_BASENAME + "\n" +
                "scp_if_ssh = True\n"; //use scp when sftp doesn't work
    }


    private Optional<BasicStringRspec.LoginService> getPrimaryLoginService(BasicStringRspec.BasicNodeInfo nodeInfo) {
        BestNodeLoginFinder.Feedback feedback = new BestNodeLoginFinder.Feedback() {
            @Override
            public void info(String s) {
                LOG.info(s);
            }

            @Override
            public void error(String s) {
                LOG.error(s);
            }

            @Override
            public void debug(String s) {
                LOG.debug(s);
            }
        };
        BestNodeLoginFinder finder = new BestNodeLoginFinder(
                basicStringRspec,
                preferredUser,
                loggedInUser,
                feedback);
        BasicStringRspec.LoginService best = finder.findBestLogin(nodeInfo.getUniqueId());

        //if no login found, try the alternative string Rspecs.
        ListIterator<BasicStringRspec> it = altBasicStringRspecs.listIterator();
        while (best == null && it.hasNext()) {
            finder = new BestNodeLoginFinder(
                    it.next(),
                    preferredUser,
                    loggedInUser,
                    feedback);
            best = finder.findBestLogin(nodeInfo.getUniqueId());
        }

        //if no proxy specified, and proxyInfo != null, force that proxy
        if (best != null && best.getSshProxy() == null && proxyInfo != null) {
            best = new BasicStringRspec.LoginService(
                    best.getAuthentication(),
                    best.getHostname(),
                    best.getPort(),
                    best.getUsername(),
                    proxyInfo
            );
        }

        return Optional.ofNullable(best);
    }

    public String getAnsibleHostFileContent() {
        String res = "# Sample commands:\n" +
                "# ansible nodes -m ping\n" +
                "# ansible nodes -m shell -a \"uptime\"\n" +
                "\n" +
                "[nodes]\n";
        List<BasicStringRspec.BasicNodeInfo> nodeInfoList = basicStringRspec.getBasicNodeInfo();
        for (BasicStringRspec.BasicNodeInfo nodeInfo : nodeInfoList) {
            Optional<BasicStringRspec.LoginService> loginService = getPrimaryLoginService(nodeInfo);
            if (loginService.isPresent()) {
                BasicStringRspec.LoginService ls = loginService.get();

                res += String.format("%s \tansible_ssh_host=%s\tansible_ssh_port=%s\tansible_ssh_user=%s\n",
                        nodeInfo.getClientId(),
                        ls.getHostname(),
                        ls.getPort(),
                        ls.getUsername());
            }
        }
        return res;
    }

    public String getFabricFileContent() {
        String res = "from fabric.api import env, run\n\n";
        List<String> allHosts = new ArrayList<>();

        res += ("#For your convenience, each server has been linked to it's client-Id by means of a role:");

        List<BasicStringRspec.BasicNodeInfo> nodeInfoList = basicStringRspec.getBasicNodeInfo();
        for (BasicStringRspec.BasicNodeInfo nodeInfo : nodeInfoList) {
            Optional<BasicStringRspec.LoginService> loginService = getPrimaryLoginService(nodeInfo);
            if (loginService.isPresent()) {
                res +=
                        ("env.roledefs['" + nodeInfo.getClientId() + "'] = ['" + loginService.get().getHostname() + "']\n");
                allHosts.add(loginService.get().getHostname());
            }
        }


        //create an 'all' role
        res += ("\nenv.roledefs['all'] = [");
        boolean first = true;
        for (String hostname : allHosts) {
            if (!first) res += ", ";
            res += "'" + hostname + "'";
            first = false;
        }
        res += ("]\n");

        res += ("\n" +
                "#Use env.hosts instead of roles if you want to execute actions on all hosts instead of being selective:\n" +
                "#env.hosts = [\n");

        for (String host : allHosts) {
            res += ("#\t\"" + host + "\",\n");
        }
        res += ("#  ]\n\n");


        res += ("\n" +
                "\n");

        switch (privateKeyHandling) {
            case COPY: {
                res += ("env.key_filename=\"./" + PRIVATE_KEY_FILE_BASENAME + "\"\n");
                break;
            }
            case LINK: {
                res += ("env.key_filename=\"" + privateKeyFile.getAbsolutePath() + "\"\n");
                break;
            }
            case DONTUSE: {
                break;
            }
            default:
                throw new RuntimeException("Unsupported privateKeyHandling -> " + privateKeyHandling);
        }

        res += ("env.use_ssh_config = True\n" +
                "env.ssh_config_path = './" + SSH_CONFIG_FILE_BASENAME + "'\n" +
                "\n" +
                "def pingtest():\n" +
                "    run('ping -c 3 8.8.8.8')\n" +
                "\n" +
                "def uptime():\n" +
                "    run('uptime')\n");

        return res;
    }

    public String getReadmeFileContent() throws IOException {
        return IOUtils.resourceToString("EXPORT_TOOLS_README.txt", this.getClass());
    }

    public String getRequirementsFileContent() {
        return "ansible==1.9.4\n";
    }

    public String getProxySshConfigContent(JFedConnection.SshProxyInfo proxy, int proxyIndex) {
        assert proxy.getSshKeyInfo() != null;
        assert proxy.getSshKeyInfo().getPrivateKey() != null;

        String privateProxyKeyFilePath;


        switch (privateKeyHandling) {
            case COPY: {
                if (proxy.getSshKeyInfo() != null &&
                        proxy.getSshKeyInfo().getPrivateKey() != null &&
                        Objects.equals(proxy.getSshKeyInfo().getPrivateKey(), privateKey))
                    privateProxyKeyFilePath = "./" + PRIVATE_KEY_FILE_BASENAME + "";
                else
                    privateProxyKeyFilePath = "./" + PROXY_PRIVATE_KEY_FILE_TEMPLATE + proxyIndex + "";
                break;
            }
            case LINK: {
                privateProxyKeyFilePath = privateKeyFile.getAbsolutePath();
                break;
            }
            case DONTUSE: {
                privateProxyKeyFilePath = null;
                break;
            }
            default:
                throw new RuntimeException("Unsupported privateKeyHandling -> " + privateKeyHandling);
        }


        String res = String.format("Host %s %s\n" +
                        "    User %s\n" +
                        "    HostName %s\n" +
                        "    Port %s\n" +
                        (privateProxyKeyFilePath == null ? "" : "    IdentityFile " + privateProxyKeyFilePath + "\n") +
                        "    ProxyCommand none\n" +
                        "    BatchMode yes\n" +
                        "    PasswordAuthentication no\n" +
                        "    EscapeChar none\n",
                proxy.getHostname(),
                "proxy" + proxyIndex,
                proxy.getUsername(),
                proxy.getHostname(),
                proxy.getPort());

        //if a proxy hostkey is provided, we can be much more secure
        if (proxy.getHostKey() != null && !proxy.getHostKey().isEmpty()) {
            res += "    UserKnownHostsFile " + SSH_PROXY_KNOWN_HOST_TEMPLATE + proxyIndex + "\n";
            res += "    CheckHostIP no\n";
            res += "    StrictHostKeyChecking yes\n";
        } else {
            //otherwise, we have to be insecure
            res += "    CheckHostIP no\n";
            res += "    StrictHostKeyChecking no\n";
        }

        res += "\n";

        return res;
    }

    public String getSshConfigFileContent() {
        String privateKeyFilePath;
        switch (privateKeyHandling) {
            case COPY: {
                privateKeyFilePath = "./" + PRIVATE_KEY_FILE_BASENAME + "";
                break;
            }
            case LINK: {
                privateKeyFilePath = privateKeyFile.getAbsolutePath();
                break;
            }
            case DONTUSE: {
                privateKeyFilePath = null;
                break;
            }
            default:
                throw new RuntimeException("Unsupported privateKeyHandling -> " + privateKeyHandling);
        }


        Set<JFedConnection.SshProxyInfo> handledProxies = new HashSet<>();
        Map<JFedConnection.SshProxyInfo, Integer> proxyIdMap = new HashMap<>();

        String res = "";
        List<BasicStringRspec.BasicNodeInfo> nodeInfoList = basicStringRspec.getBasicNodeInfo();
        for (BasicStringRspec.BasicNodeInfo nodeInfo : nodeInfoList) {
            Optional<BasicStringRspec.LoginService> loginService = getPrimaryLoginService(nodeInfo);
            if (loginService.isPresent()) {
                BasicStringRspec.LoginService ls = loginService.get();

                if (ls.getSshProxy() != null && handledProxies.add(ls.getSshProxy())) {
                    res += getProxySshConfigContent(ls.getSshProxy(), handledProxies.size() - 1);
                    proxyIdMap.put(ls.getSshProxy(), handledProxies.size() - 1);
                }

                res += (String.format("Host %s %s\n" +
                                "    HostName %s\n" +
                                "    Port %s\n" +
                                "    User %s\n" +
                                (privateKeyFilePath == null ? "" : "    IdentityFile " + privateKeyFilePath + "\n") +
                                "    ForwardAgent yes\n",
                        nodeInfo.getClientId(),
                        ls.getHostname(),
                        ls.getHostname(),
                        ls.getPort(),
                        ls.getUsername()));

                if (ls.getSshProxy() != null) {
                    JFedConnection.SshProxyInfo proxy = ls.getSshProxy();
                    int proxyId = proxyIdMap.get(proxy);
                    if (SshVersionFinder.checkIfVersionSupportsNc() == Boolean.TRUE)
                        res +=
                                "    ProxyCommand            ssh -F " + SSH_CONFIG_FILE_BASENAME + " proxy" + proxyId + " -W %h:%p\n";
                    else
                        res +=
                                "    ProxyCommand            ssh -F " + SSH_CONFIG_FILE_BASENAME + " proxy" + proxyId + " nc -w 5 %h %p\n";
//                        res += "    ProxyCommand            ssh -e none -oPort="+proxy.getPort()+" "+proxy.getUsername()+"@"+proxy.getHostname()+" nc -w 5 %h %p\n";
                    res += "    TCPKeepAlive            yes\n";
                    res += "    Compression             no\n";
                }

                //sadly, we don't know the key, so we have to be insecure
                res += "    CheckHostIP no\n";
                res += "    StrictHostKeyChecking no\n";

                res += "\n";
            }
        }
        return res;
    }

    public String getKnownHostLine(JFedConnection.SshProxyInfo proxyInfo) {
        assert proxyInfo.getHostKey() != null;

        String hostkey = proxyInfo.getHostKey().trim();
        //maybe it is already a known_hosts line?
        if (hostkey.split(" ").length >= 3) {
            String[] parts = hostkey.split(" ");
            if (parts[1].startsWith("ecdsa") || parts[1].startsWith("ssh-")) {
                String newHostkey = "";
                for (int i = 1; i < parts.length; i++)
                    newHostkey += parts[i] + " ";
                hostkey = newHostkey;
            }
        }
        if (!hostkey.startsWith("ssh-") && !hostkey.startsWith("ecdsa"))
            hostkey = "ssh-rsa " + hostkey; //guess it is ssh-rsa

        if (proxyInfo.getPort() == 22)
            return proxyInfo.getHostname() + " " + hostkey + "\n";
        else
            return "[" + proxyInfo.getHostname() + "]:" + proxyInfo.getPort() + " " + hostkey + "\n";
    }

    private List<JFedConnection.SshProxyInfo> proxiesByIndex() {
        List<JFedConnection.SshProxyInfo> res = new ArrayList<>();

        Set<JFedConnection.SshProxyInfo> handledProxies = new HashSet<>();

        List<BasicStringRspec.BasicNodeInfo> nodeInfoList = basicStringRspec.getBasicNodeInfo();
        for (BasicStringRspec.BasicNodeInfo nodeInfo : nodeInfoList) {
            Optional<BasicStringRspec.LoginService> loginService = getPrimaryLoginService(nodeInfo);
            if (loginService.isPresent()) {
                BasicStringRspec.LoginService ls = loginService.get();

                if (ls.getSshProxy() != null && handledProxies.add(ls.getSshProxy())) {
                    res.add(ls.getSshProxy());
                }
            }
        }
        return res;
    }

    /**
     * returns a string with feedback on what happened
     */
    public String writeFilesToZip(File zipFile) {
        assert privateKeyHandling == PrivateKeyHandling.COPY
                || privateKeyHandling == PrivateKeyHandling.DONTUSE;

        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
            OutputStreamWriter writer = new OutputStreamWriter(zos);

            if (privateKeyHandling == PrivateKeyHandling.COPY && privateKey != null) {
                zos.putNextEntry(new ZipEntry(PRIVATE_KEY_FILE_BASENAME));
                writer.write(KeyUtil.privateKeyToAnyPem(privateKey));
                writer.flush();
                zos.closeEntry();
            }

            if (publicKey != null) {
                final String publicKeyStr = KeyUtil.publicKeyToOpenSshAuthorizedKeysFormat(this.publicKey);

                if (publicKeyStr != null) {
                    zos.putNextEntry(new ZipEntry(PUBLIC_KEY_FILE_BASENAME));
                    writer.write(publicKeyStr);
                    writer.flush();
                    zos.closeEntry();
                } else {
                    LOG.warn("Could not convert publicKey to OpenSSH format. Skipping");
                }
            }

            zos.putNextEntry(new ZipEntry(ANSIBLE_CONFIG_FILE_BASENAME));
            writer.write(getAnsibleConfigFileContent());
            writer.flush();
            zos.closeEntry();

            zos.putNextEntry(new ZipEntry(ANSIBLE_HOSTS_FILE_BASENAME));
            writer.write(getAnsibleHostFileContent());
            writer.flush();
            zos.closeEntry();

            zos.putNextEntry(new ZipEntry(FABRIC_FILE_BASENAME));
            writer.write(getFabricFileContent());
            writer.flush();
            zos.closeEntry();

            zos.putNextEntry(new ZipEntry(SSH_CONFIG_FILE_BASENAME));
            writer.write(getSshConfigFileContent());
            writer.flush();
            zos.closeEntry();

            zos.putNextEntry(new ZipEntry(README_FILE_BASENAME));
            writer.write(getReadmeFileContent());
            writer.flush();
            zos.closeEntry();

            zos.putNextEntry(new ZipEntry(REQUIREMENTS_FILE_BASENAME));
            writer.write(getRequirementsFileContent());
            writer.flush();
            zos.closeEntry();

            int proxyIndex = 0;
            for (JFedConnection.SshProxyInfo proxyInfo : proxiesByIndex()) {
                if (proxyInfo.getHostKey() != null && !proxyInfo.getHostKey().isEmpty()) {
                    String filename = SSH_PROXY_KNOWN_HOST_TEMPLATE + proxyIndex;
                    zos.putNextEntry(new ZipEntry(filename));
                    writer.write(getKnownHostLine(proxyInfo));
                    writer.flush();
                    zos.closeEntry();
                }

                if (privateKeyHandling == PrivateKeyHandling.COPY &&
                        proxyInfo.getSshKeyInfo() != null &&
                        proxyInfo.getSshKeyInfo().getPrivateKey() != null &&
                        !Objects.equals(proxyInfo.getSshKeyInfo().getPrivateKey(), privateKey)) {
                    zos.putNextEntry(new ZipEntry(PROXY_PRIVATE_KEY_FILE_TEMPLATE + proxyIndex));
                    writer.write(KeyUtil.privateKeyToAnyPem(proxyInfo.getSshKeyInfo().getPrivateKey()));
                    writer.flush();
                    zos.closeEntry();
                }
                proxyIndex++;
            }

            return String.format("Wrote tool configuration files to '%s'", zipFile.getAbsolutePath());
        } catch (IOException ex) {
            LOG.error("Error while writing tool configuration files", ex);
            return "Error while writing tool configuration files";
        }
    }

    public void writeFilesToDir(File ansibleDir) throws IOException {
        assert ansibleDir != null;
        assert ansibleDir.exists();

        IOUtils.stringToFile(
                new File(ansibleDir, ANSIBLE_CONFIG_FILE_BASENAME),
                getAnsibleConfigFileContent());

        if (publicKey != null) {
            IOUtils.stringToFile(
                    new File(ansibleDir, PUBLIC_KEY_FILE_BASENAME),
                    getPublicKeyFileContent()
            );
        }

        IOUtils.stringToFile(
                new File(ansibleDir, ANSIBLE_HOSTS_FILE_BASENAME),
                getAnsibleHostFileContent()
        );

        IOUtils.stringToFile(
                new File(ansibleDir, FABRIC_FILE_BASENAME),
                getFabricFileContent()
        );

        IOUtils.stringToFile(
                new File(ansibleDir, SSH_CONFIG_FILE_BASENAME),
                getSshConfigFileContent()
        );

        IOUtils.stringToFile(
                new File(ansibleDir, REQUIREMENTS_FILE_BASENAME),
                getRequirementsFileContent()
        );

        int proxyIndex = 0;
        for (JFedConnection.SshProxyInfo proxyInfo : proxiesByIndex()) {
            if (proxyInfo.getHostKey() != null && !proxyInfo.getHostKey().isEmpty()) {
                String filename = SSH_PROXY_KNOWN_HOST_TEMPLATE + proxyIndex;
                IOUtils.stringToFile(
                        new File(ansibleDir, filename),
                        getKnownHostLine(proxyInfo)
                );
            }

            if (privateKeyHandling == PrivateKeyHandling.COPY &&
                    proxyInfo.getSshKeyInfo() != null &&
                    proxyInfo.getSshKeyInfo().getPrivateKey() != null &&
                    !Objects.equals(proxyInfo.getSshKeyInfo().getPrivateKey(), privateKey)) {
                File privKeyFile = new File(ansibleDir, PROXY_PRIVATE_KEY_FILE_TEMPLATE + proxyIndex);
                IOUtils.stringToFile(privKeyFile, new String(KeyUtil.privateKeyToAnyPem(proxyInfo.getSshKeyInfo().getPrivateKey())));
                IOUtils.assureUserOnlyPerms(privKeyFile, true, true/*allow overwrite next time*/, false);
            }
            proxyIndex++;
        }

        if (privateKeyHandling == PrivateKeyHandling.COPY && privateKey != null) {
            File privKeyFile = new File(ansibleDir, PRIVATE_KEY_FILE_BASENAME);
            IOUtils.stringToFile(privKeyFile, new String(KeyUtil.privateKeyToAnyPem(privateKey)));
            IOUtils.assureUserOnlyPerms(privKeyFile, true, true/*allow overwrite next time*/, false);
        }
    }
}
