Skip to content

Commit

Permalink
Merge pull request #176 from jtobard/dev/retry-failed-execution
Browse files Browse the repository at this point in the history
Run execution with just failed nodes
  • Loading branch information
gschueler authored May 11, 2018
2 parents 8866cd0 + a1ddd43 commit 2c99f13
Show file tree
Hide file tree
Showing 7 changed files with 395 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/main/java/org/rundeck/client/api/RundeckApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -1047,4 +1047,12 @@ Call<Void> deleteReadme(
@GET("user/list")
Call<List<User>> listUsers();

@Headers("Accept: application/json")
@POST("job/{id}/retry/{eid}")
Call<Execution> retryJob(
@Path("id") String id,
@Path("eid") String eid,
@Body ExecRetry execRetry

);
}
51 changes: 51 additions & 0 deletions src/main/java/org/rundeck/client/api/model/ExecRetry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2018 Rundeck, Inc. (http://rundeck.com)
*
* 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.rundeck.client.api.model;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;



/**
* Parameters to run a job
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ExecRetry extends JobRun{
private String failedNodes;

public String getFailedNodes() {
return failedNodes;
}

public void setFailedNodes(String failedNodes) {
this.failedNodes = failedNodes;
}

@Override
public String toString() {
if (null != failedNodes) {
return "org.rundeck.client.api.model.ExecRetry{" +
"failedNodes='" + failedNodes + '\'' +
'}';
} else {
return super.toString();
}
}
}
3 changes: 2 additions & 1 deletion src/main/java/org/rundeck/client/tool/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ public static Tool tool(final Rd rd) {
new Tokens(rd),
new Nodes(rd),
new Users(rd),
new Something()
new Something(),
new Retry(rd)

)
.bannerResource("rd-banner.txt")
Expand Down
135 changes: 135 additions & 0 deletions src/main/java/org/rundeck/client/tool/commands/Retry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright 2018 Rundeck, Inc. (http://rundeck.com)
*
* 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.rundeck.client.tool.commands;

import org.rundeck.client.api.model.ExecRetry;
import org.rundeck.client.api.model.Execution;
import org.rundeck.client.api.model.JobFileUploadResult;
import org.rundeck.client.api.model.JobRun;
import org.rundeck.client.tool.RdApp;
import org.rundeck.client.tool.commands.jobs.Files;
import org.rundeck.client.tool.options.RetryBaseOptions;
import org.rundeck.toolbelt.Command;
import org.rundeck.toolbelt.CommandOutput;
import org.rundeck.toolbelt.InputError;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
* retry subcommand
*/
@Command(description = "Run a Job based on a specific execution. Specify option arguments after -- as \"-opt value\". Upload files as \"-opt " +
"@path\" or \"-opt@ path\". If they aren't specified, the options are going to be overridden by the execution options")
public class Retry extends AppCommand {

public static final int SEC_MS = 1000;
public static final int MIN_MS = 60 * 1000;
public static final int HOUR_MS = 60 * 60 * 1000;
public static final int DAY_MS = 24 * 60 * 60 * 1000;
public static final int WEEK_MS = 7 * 24 * 60 * 60 * 1000;

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

@Command(isDefault = true, isSolo = true)
public boolean retry(RetryBaseOptions options, CommandOutput out) throws IOException, InputError {
requireApiVersion("retry", 24);
String jobId = Run.getJobIdFromOpts(options, out, getRdApp(), () -> projectOrEnv(options));
String execId = options.getEid();
if (null == jobId) {
return false;
}
Execution execution;

ExecRetry request = new ExecRetry();
request.setLoglevel(options.getLoglevel());
request.setAsUser(options.getUser());
request.setFailedNodes(options.getFailedNodes());
List<String> commandString = options.getCommandString();
Map<String, String> jobopts = new HashMap<>();
Map<String, File> fileinputs = new HashMap<>();
String key = null;
if (null != commandString) {
boolean isfile = false;
for (String part : commandString) {
if (key == null && part.startsWith("-")) {
key = part.substring(1);
if (key.endsWith("@")) {
key = key.substring(0, key.length() - 1);
isfile = true;
}
} else if (key != null) {
String filepath = null;
if (part.charAt(0) == '@' && !isfile) {
//file input
filepath = part.substring(1);
isfile = true;
}
if (isfile) {
File file = new File(filepath != null ? filepath : part);
fileinputs.put(key, file);
}
jobopts.put(key, part);
key = null;
isfile = false;
}
}
}
if (key != null) {
throw new InputError(
String.format(
"Incorrect job options, expected: \"-%s value\", but saw only \"-%s\"",
key,
key
));
}
if (fileinputs.size() > 0) {
for (String optionName : fileinputs.keySet()) {
File file = fileinputs.get(optionName);
if (Files.invalidInputFile(file)) {
throw new InputError("File Option -" + optionName + ": File cannot be read: " + file);
}
}
for (String optionName : fileinputs.keySet()) {
File file = fileinputs.get(optionName);
JobFileUploadResult jobFileUploadResult = Files.uploadFileForJob(
getRdApp(),
file,
jobId,
optionName
);
String fileid = jobFileUploadResult.getFileIdForOption(optionName);
jobopts.put(optionName, fileid);
out.info(String.format("File Upload OK (%s -> %s)", file, fileid));
}
}

request.setOptions(jobopts);
execution = apiCall(api -> api.retryJob(jobId, execId, request));

String started = "started";
out.info(String.format("Execution %s: %s%n", started, execution.toBasicString()));

return Executions.maybeFollow(getRdApp(), options, execution.getId(), out);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2018 Rundeck, Inc. (http://rundeck.com)
*
* 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.rundeck.client.tool.options;

import com.lexicalscope.jewel.cli.CommandLineInterface;
import com.lexicalscope.jewel.cli.Option;
import com.lexicalscope.jewel.cli.Unparsed;
import org.rundeck.client.api.model.DateInfo;

import java.util.List;

@CommandLineInterface(application = "retry")
public interface RetryBaseOptions extends JobIdentOptions, FollowOptions, RetryExecutionOption {
@Option(shortName = "l",
longName = "logevel",
description = "Run the command using the specified LEVEL. LEVEL can be verbose, info, warning, error.",
defaultValue = {"info"},
pattern = "(verbose|info|warning|error)")
String getLoglevel();


@Option(shortName = "u", longName = "user", description = "A username to run the job as, (runAs access required).")
String getUser();

boolean isUser();

@Option(shortName = "F", longName = "failedNodes", description = "Run only on failed nodes (default=true).",
defaultValue = {"true"},
pattern = "(true|false)")
String getFailedNodes();


@Unparsed(name = "-- -OPT VAL -OPT2 VAL -OPTFILE @filepath", description = "Job options")
List<String> getCommandString();

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2018 Rundeck, Inc. (http://rundeck.com)
*
* 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.rundeck.client.tool.options;

import com.lexicalscope.jewel.cli.Option;

/**
* ID for execution
*/
public interface RetryExecutionOption {

@Option(shortName = "e", longName = "eid", description = "Execution ID to retry on failed nodes.")
String getEid();
}
Loading

0 comments on commit 2c99f13

Please sign in to comment.