<commit_before>package org.rundeck.client.tool.commands;

import com.lexicalscope.jewel.cli.CommandLineInterface;
import com.lexicalscope.jewel.cli.Option;
import com.simplifyops.toolbelt.Command;
import com.simplifyops.toolbelt.CommandOutput;
import com.simplifyops.toolbelt.InputError;
import org.rundeck.client.api.RundeckApi;
import org.rundeck.client.api.model.*;
import org.rundeck.client.tool.AppConfig;
import org.rundeck.client.tool.RdApp;
import org.rundeck.client.tool.options.*;
import org.rundeck.client.util.Client;
import org.rundeck.client.util.Format;
import retrofit2.Call;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * Created by greg on 5/20/16.
 */

@Command(description = "List running executions, attach and follow their output, or kill them.")
public class Executions extends AppCommand {

    public Executions(final RdApp client) {
        super(client);
    }

    @CommandLineInterface(application = "kill") interface Kill extends ExecutionIdOption {

    }

    @Command(description = "Attempt to kill an execution by ID.")
    public boolean kill(Kill options, CommandOutput out) throws IOException, InputError {
        if (null == options.getId()) {
            throw new InputError("-e is required");
        }
        AbortResult abortResult = apiCall(api -> api.abortExecution(options.getId()));
        AbortResult.Reason abort = abortResult.abort;
        Execution execution = abortResult.execution;
        boolean failed = null != abort && "failed".equals(abort.status);

        out.output(String.format("Kill [%s] result: %s", options.getId(), abort.status));

        if (null != execution) {
            out.output(String.format("Execution [%s] status: %s", options.getId(), execution.getStatus()));
        }

        if (failed) {
            out.warning(String.format("Kill request failed: %s", abort.reason));
        }
        return !failed;
    }

    @CommandLineInterface(application = "delete") interface Delete extends ExecutionIdOption {

    }

    @Command(description = "Delete an execution by ID.")
    public void delete(Delete options, CommandOutput out) throws IOException, InputError {
        apiCall(api -> api.deleteExecution(options.getId()));
        out.info(String.format("Delete [%s] succeeded.", options.getId()));
    }

    @CommandLineInterface(application = "follow") interface Follow extends ExecutionsFollowOptions {

    }


    @Command(description = "Follow the output of an execution. Restart from the beginning, or begin tailing as it " +
                           "runs.")
    public boolean follow(Follow options, CommandOutput out) throws IOException, InputError {

        int max = 500;

        Call<ExecOutput> output = startFollowOutput(
                getClient(),
                max,
                options.isRestart(),
                options.getId(),
                options.getTail()
        );


        return followOutput(getClient(), output, options.isProgress(), options.isQuiet(), options.getId(), max, out);
    }

    public static Call<ExecOutput> startFollowOutput(
            final Client<RundeckApi> client,
            final long max,
            final boolean restart,
            final String id,
            final long tail
    )
    {

        Call<ExecOutput> out;
        if (restart) {
            out = client.getService().getOutput(id, 0L, 0L, max);
        } else {
            out = client.getService().getOutput(id, tail);
        }
        return out;
    }

    public static boolean followOutput(
            final Client<RundeckApi> client,
            final Call<ExecOutput> output,
            final boolean progress,
            final boolean quiet,
            final String id,
            long max,
            CommandOutput out
    ) throws IOException
    {
        boolean done = false;
        String status = null;
        Call<ExecOutput> callOutput = output;
        while (!done) {
            ExecOutput execOutput = client.checkError(callOutput);
            printLogOutput(execOutput.entries, progress, quiet, out);
            status = execOutput.execState;
            done = !"running".equals(status);
            if (!done) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    break;
                }

                callOutput = client.getService().getOutput(id, execOutput.offset, execOutput.lastModified, max);
            }
        }
        return "succeeded".equals(status);
    }

    private static void printLogOutput(
            final List<ExecLog> entries,
            final boolean progress,
            final boolean quiet,
            CommandOutput out
    )
    {

        if (!quiet && !progress) {
            for (ExecLog entry : entries) {
                out.output(entry.log);
            }
        } else if (progress && entries.size() > 0) {<commit_msg>Fix #65 run -f halts before job execution finishes<commit_after>package org.rundeck.client.tool.commands;

import com.lexicalscope.jewel.cli.CommandLineInterface;
import com.lexicalscope.jewel.cli.Option;
import com.simplifyops.toolbelt.Command;
import com.simplifyops.toolbelt.CommandOutput;
import com.simplifyops.toolbelt.InputError;
import org.rundeck.client.api.RundeckApi;
import org.rundeck.client.api.model.*;
import org.rundeck.client.tool.AppConfig;
import org.rundeck.client.tool.RdApp;
import org.rundeck.client.tool.options.*;
import org.rundeck.client.util.Client;
import org.rundeck.client.util.Format;
import retrofit2.Call;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * Created by greg on 5/20/16.
 */

@Command(description = "List running executions, attach and follow their output, or kill them.")
public class Executions extends AppCommand {

    public Executions(final RdApp client) {
        super(client);
    }

    @CommandLineInterface(application = "kill") interface Kill extends ExecutionIdOption {

    }

    @Command(description = "Attempt to kill an execution by ID.")
    public boolean kill(Kill options, CommandOutput out) throws IOException, InputError {
        if (null == options.getId()) {
            throw new InputError("-e is required");
        }
        AbortResult abortResult = apiCall(api -> api.abortExecution(options.getId()));
        AbortResult.Reason abort = abortResult.abort;
        Execution execution = abortResult.execution;
        boolean failed = null != abort && "failed".equals(abort.status);

        out.output(String.format("Kill [%s] result: %s", options.getId(), abort.status));

        if (null != execution) {
            out.output(String.format("Execution [%s] status: %s", options.getId(), execution.getStatus()));
        }

        if (failed) {
            out.warning(String.format("Kill request failed: %s", abort.reason));
        }
        return !failed;
    }

    @CommandLineInterface(application = "delete") interface Delete extends ExecutionIdOption {

    }

    @Command(description = "Delete an execution by ID.")
    public void delete(Delete options, CommandOutput out) throws IOException, InputError {
        apiCall(api -> api.deleteExecution(options.getId()));
        out.info(String.format("Delete [%s] succeeded.", options.getId()));
    }

    @CommandLineInterface(application = "follow") interface Follow extends ExecutionsFollowOptions {

    }


    @Command(description = "Follow the output of an execution. Restart from the beginning, or begin tailing as it " +
                           "runs.")
    public boolean follow(Follow options, CommandOutput out) throws IOException, InputError {

        int max = 500;

        Call<ExecOutput> output = startFollowOutput(
                getClient(),
                max,
                options.isRestart(),
                options.getId(),
                options.getTail()
        );


        return followOutput(getClient(), output, options.isProgress(), options.isQuiet(), options.getId(), max, out);
    }

    public static Call<ExecOutput> startFollowOutput(
            final Client<RundeckApi> client,
            final long max,
            final boolean restart,
            final String id,
            final long tail
    )
    {

        Call<ExecOutput> out;
        if (restart) {
            out = client.getService().getOutput(id, 0L, 0L, max);
        } else {
            out = client.getService().getOutput(id, tail);
        }
        return out;
    }

    public static boolean followOutput(
            final Client<RundeckApi> client,
            final Call<ExecOutput> output,
            final boolean progress,
            final boolean quiet,
            final String id,
            long max,
            CommandOutput out
    ) throws IOException
    {
        boolean done = false;
        String status = null;
        Call<ExecOutput> callOutput = output;
        while (!done) {
            ExecOutput execOutput = client.checkError(callOutput);
            printLogOutput(execOutput.entries, progress, quiet, out);
            status = execOutput.execState;
            done = execOutput.execCompleted && execOutput.completed;
            if (!done) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    break;
                }

                callOutput = client.getService().getOutput(id, execOutput.offset, execOutput.lastModified, max);
            }
        }
        return "succeeded".equals(status);
    }

    private static void printLogOutput(
            final List<ExecLog> entries,
            final boolean progress,
            final boolean quiet,
            CommandOutput out
    )
    {

        if (!quiet && !progress) {
            for (ExecLog entry : entries) {
                out.output(entry.log);
            }
        } else if (progress && entries.size() > 0) {
{'fraction_non_alphanumeric': 0.10465578989255869, 'fraction_numerical': 0.0033824114604058893, 'mean_word_length': 2.3036477160696682, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '2368759', 'n_tokens_mistral': 2726, 'n_tokens_neox': 2593, 'n_words': 679}