Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUN-1634: Fix exit codes on command failures #501

Merged
merged 5 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions rd-cli-acl/src/main/java/org/rundeck/client/ext/acl/Acl.java
Original file line number Diff line number Diff line change
Expand Up @@ -477,9 +477,9 @@ static class TestOptions
}

@CommandLine.Command(description = "Test ACL Policies")
public boolean test(@CommandLine.Mixin TestOptions opts) {
public int test(@CommandLine.Mixin TestOptions opts) {
if (!applyArgValidate(opts)) {
return false;
return 2;
}
final RuleEvaluator authorization = createAuthorization(opts);
AuthRequest authRequest = createAuthRequestFromArgs(opts);
Expand Down Expand Up @@ -562,7 +562,7 @@ public boolean test(@CommandLine.Mixin TestOptions opts) {
);
}
log("The test " + (testPassed ? "passed" : "failed"));
return testPassed;
return testPassed?0:2;
}

@CommandLine.Command(description = "Create ACL Policies")
Expand All @@ -580,11 +580,11 @@ public void create(@CommandLine.Mixin AclCreateOptions opts) throws IOException
}

@CommandLine.Command(description = "Validate ACL Policies")
public boolean validate(@CommandLine.Mixin AclOptions opts) {
public int validate(@CommandLine.Mixin AclOptions opts) {
final Validation validation = validatePolicies(opts);
reportValidation(validation);
log("The validation " + (validation.isValid() ? "passed" : "failed"));
return validation.isValid();
return validation.isValid() ? 0 : 2;
}

private HashSet<Map<String, String>> resources(final Map<String, String> resourceMap) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import java.util.concurrent.Callable;

@CommandLine.Command(description = "Install a plugin from your plugin repository into your Rundeck instance", name = "install")
public class InstallPlugin implements RdCommandExtension, RdOutput, Callable<Boolean> {
public class InstallPlugin implements RdCommandExtension, RdOutput, Callable<Integer> {
@Setter
private RdTool rdTool;

Expand All @@ -40,7 +40,7 @@ public class InstallPlugin implements RdCommandExtension, RdOutput, Callable<Boo
@CommandLine.Option(names = {"--version", "-v"}, description = "(Optional) Specific version of the plugin you want to install")
String version;

public Boolean call() throws InputError, IOException {
public Integer call() throws InputError, IOException {
RepositoryResponseHandler.handle(
rdTool.apiWithErrorResponse(api -> {
if (version != null) {
Expand All @@ -49,7 +49,7 @@ public Boolean call() throws InputError, IOException {
return api.installPlugin(repoName, pluginId);
}
}), rdOutput);
return true;
return 0;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import java.util.concurrent.Callable;

@CommandLine.Command(description = "Unistall a Rundeck plugin from your Rundeck instance", name = "uninstall")
public class UninstallPlugin implements Callable<Boolean>, RdCommandExtension, RdOutput {
public class UninstallPlugin implements Callable<Integer>, RdCommandExtension, RdOutput {
@Setter
private RdTool rdTool;
@Setter
Expand All @@ -38,10 +38,10 @@ public class UninstallPlugin implements Callable<Boolean>, RdCommandExtension, R
String pluginId;


public Boolean call() throws InputError, IOException {
public Integer call() throws InputError, IOException {
RepositoryResponseHandler.handle(
rdTool.apiWithErrorResponse(api -> api.uninstallPlugin(pluginId)), rdOutput
);
return true;
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import java.util.concurrent.Callable;

@CommandLine.Command(description = "Upload a Rundeck plugin to your plugin repository", name = "upload")
public class UploadPlugin extends BaseCommand implements Callable<Boolean> {
public class UploadPlugin extends BaseCommand implements Callable<Integer> {

@CommandLine.Option(names = {"-r", "--repository"}, description = "Target name of repository to upload plugin into.", required = true)
String repoName;
Expand All @@ -35,13 +35,13 @@ public class UploadPlugin extends BaseCommand implements Callable<Boolean> {
String binaryPath;


public Boolean call() throws InputError, IOException {
public Integer call() throws InputError, IOException {
File binary = new File(binaryPath);
if (!binary.exists())
throw new IOException(String.format("Unable to find specified file: %s", binaryPath));
RequestBody fileUpload = RequestBody.create(binary, Client.MEDIA_TYPE_OCTET_STREAM);

RepositoryResponseHandler.handle(getRdTool().apiWithErrorResponse(api -> api.uploadPlugin(repoName, fileUpload)), getRdOutput());
return true;
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ class InstallPluginTest extends Specification {
installCmd.pluginId = 'bcf8885df1e8'

when:
installCmd.call()
def result = installCmd.call()

then:
1 * api.installPlugin("private", 'bcf8885df1e8') >> Calls.response(new ArtifactActionMessage(msg: "Plugin Installed"))
1 * out.output('Plugin Installed')
result == 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ class UninstallPluginTest extends Specification {


when:
uninstallCmd.call()
def result=uninstallCmd.call()

then:
1 * api.uninstallPlugin(_) >> Calls.response(new ArtifactActionMessage(msg: "Plugin Uninstalled"))
1 * out.output('Plugin Uninstalled')
result==0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ class UploadPluginTest extends Specification {


when:
uploadCmd.call()
def result = uploadCmd.call()

then:
1 * api.uploadPlugin("private", _) >> Calls.response(new ArtifactActionMessage(msg: "Upload succeeded"))
1 * out.output('Upload succeeded')
result == 0
}
}
2 changes: 1 addition & 1 deletion rd-cli-enterprise/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dependencies {
implementation libs.picocli
annotationProcessor libs.picocliCodegen

testImplementation project(":rd-cli-lib"), project(":rd-api-client")
testImplementation project(":rd-cli-lib"), project(":rd-api-client"), project(":rd-testing")

testImplementation libs.retrofitMock
testImplementation libs.okhttpMockwebserver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ static class Options {
}

@CommandLine.Command(description = "Set cluster member execution mode Active")
public boolean active(@CommandLine.Mixin Options options) throws IOException, InputError {
return changeMode(ExecutionMode.active, options, EnterpriseApi::executionModeEnable);
public int active(@CommandLine.Mixin Options options) throws IOException, InputError {
return changeMode(ExecutionMode.active, options, EnterpriseApi::executionModeEnable) ? 0 : 1;
}


@CommandLine.Command(description = "Set cluster member execution mode Passive")
public boolean passive(@CommandLine.Mixin Options options) throws IOException, InputError {
return changeMode(ExecutionMode.passive, options, EnterpriseApi::executionModeDisable);
public int passive(@CommandLine.Mixin Options options) throws IOException, InputError {
return changeMode(ExecutionMode.passive, options, EnterpriseApi::executionModeDisable) ? 0 : 1;
}

boolean changeMode(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ static class StatusOpts {
}

@CommandLine.Command(description = "license status")
public boolean status(@CommandLine.Mixin StatusOpts opts) throws InputError, IOException {
public int status(@CommandLine.Mixin StatusOpts opts) throws InputError, IOException {
RdTool.apiVersionCheck("license status", 41, getClient().getApiVersion());
LicenseResponse response = getClient().apiCall(EnterpriseApi::verifyLicense);

Expand Down Expand Up @@ -85,9 +85,9 @@ public boolean status(@CommandLine.Mixin StatusOpts opts) throws InputError, IOE
opts.getRemaining()
));
}
return false;
return 1;
}
return !opts.isStatus() || response.isActive();
return (!opts.isStatus() || response.isActive()) ? 0 : 1;
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.rundeck.client.tool.commands.enterprise.cluster

import groovy.transform.CompileStatic
import org.rundeck.client.api.model.ExecutionMode
import org.rundeck.client.testing.MockRdTool
import org.rundeck.client.tool.CommandOutput
import org.rundeck.client.tool.RdApp
import org.rundeck.client.tool.commands.enterprise.api.EnterpriseApi
import org.rundeck.client.tool.commands.enterprise.api.model.EnterpriseModeResponse
import org.rundeck.client.tool.commands.enterprise.api.model.LicenseResponse
import org.rundeck.client.tool.commands.enterprise.license.License
import org.rundeck.client.tool.extension.RdTool
import org.rundeck.client.util.Client
import org.rundeck.client.util.RdClientConfig
import retrofit2.Retrofit
import retrofit2.mock.Calls
import spock.lang.Specification

class ModeSpec extends Specification {
private RdTool setupMock(EnterpriseApi api, CommandOutput out, int apiVersion) {
def retrofit = new Retrofit.Builder().baseUrl('http://example.com/fake/').build()
def client = new Client(api, retrofit, null, null, apiVersion, true, null)
def rdapp = Mock(RdApp) {
getClient(EnterpriseApi) >> client
getAppConfig() >> Mock(RdClientConfig)
getOutput() >> out
}
def rdTool = new MockRdTool(client: client, rdApp: rdapp)
rdTool.appConfig = Mock(RdClientConfig)
rdTool
}

def "active"() {
def api = Mock(EnterpriseApi)
def out = Mock(CommandOutput)
RdTool rdTool = setupMock(api, out, 41)
Mode command = new Mode()
command.rdTool = rdTool
def opts = new Mode.Options()
opts.uuid = 'uuid'


when:
def result = command.active(opts)

then:
1 * api.executionModeEnable('uuid') >> Calls.response(new EnterpriseModeResponse(executionMode: respstatus as ExecutionMode))
0 * api._(*_)
result == expected
where:
respstatus | expected
'active' | 0
'passive' | 1
}
def "passive"() {
def api = Mock(EnterpriseApi)
def out = Mock(CommandOutput)
RdTool rdTool = setupMock(api, out, 41)
Mode command = new Mode()
command.rdTool = rdTool
def opts = new Mode.Options()
opts.uuid = 'uuid'


when:
def result = command.passive(opts)

then:
1 * api.executionModeDisable('uuid') >> Calls.response(new EnterpriseModeResponse(executionMode: respstatus as ExecutionMode))
0 * api._(*_)
result == expected
where:
respstatus | expected
'active' | 1
'passive' | 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.rundeck.client.tool.commands.enterprise.license

import groovy.transform.CompileStatic
import okhttp3.ResponseBody
import org.rundeck.client.api.RundeckApi
import org.rundeck.client.api.model.KeyStorageItem
import org.rundeck.client.testing.MockRdTool
import org.rundeck.client.tool.CommandOutput
import org.rundeck.client.tool.RdApp
import org.rundeck.client.tool.commands.enterprise.api.EnterpriseApi
import org.rundeck.client.tool.commands.enterprise.api.model.LicenseResponse
import org.rundeck.client.tool.extension.RdTool
import org.rundeck.client.util.Client
import org.rundeck.client.util.RdClientConfig
import retrofit2.Retrofit
import retrofit2.mock.Calls
import spock.lang.Specification

class LicenseSpec extends Specification {
private RdTool setupMock(EnterpriseApi api, CommandOutput out, int apiVersion) {
def retrofit = new Retrofit.Builder().baseUrl('http://example.com/fake/').build()
def client = new Client(api, retrofit, null, null, apiVersion, true, null)
def rdapp = Mock(RdApp) {
getClient(EnterpriseApi) >> client
getAppConfig() >> Mock(RdClientConfig)
getOutput() >> out
}
def rdTool = new MockRdTool(client: client, rdApp: rdapp)
rdTool.appConfig = Mock(RdClientConfig)
rdTool
}

def "status"() {
def api = Mock(EnterpriseApi)
def out = Mock(CommandOutput)
RdTool rdTool = setupMock(api, out, 41)
License command = new License()
command.rdTool = rdTool
def opts = new License.StatusOpts()


when:
def result = command.status(opts)

then:
1 * api.verifyLicense() >> Calls.response(new LicenseResponse())
0 * api._(*_)
result == 0
}

def "status remaining"() {
def api = Mock(EnterpriseApi)
def out = Mock(CommandOutput)
RdTool rdTool = setupMock(api, out, 41)
License command = new License()
command.rdTool = rdTool
def opts = new License.StatusOpts()
opts.remaining = optremain


when:
def result = command.status(opts)

then:
1 * api.verifyLicense() >> Calls.response(new LicenseResponse(remaining: remaining))
0 * api._(*_)
result == expect
where:
optremain | remaining | expect
10 | 10 | 0
10 | 9 | 1
10 | 0 | 1
}

def "status expired"() {
def api = Mock(EnterpriseApi)
def out = Mock(CommandOutput)
RdTool rdTool = setupMock(api, out, 41)
License command = new License()
command.rdTool = rdTool
def opts = new License.StatusOpts()
opts.status = true


when:
def result = command.status(opts)

then:
1 * api.verifyLicense() >> Calls.response(new LicenseResponse(active: active))
0 * api._(*_)
result == expect
where:
active | expect
true | 0
false | 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
*/

@CommandLine.Command(description = "Run adhoc command or script on matching nodes.", name = "adhoc", showEndOfOptionsDelimiterInUsageHelp = true)
public class Adhoc extends BaseCommand implements Callable<Boolean> {
public class Adhoc extends BaseCommand implements Callable<Integer> {

@CommandLine.Mixin
AdhocBaseOptions options;
Expand All @@ -51,7 +51,7 @@ public class Adhoc extends BaseCommand implements Callable<Boolean> {
@CommandLine.Mixin
NodeFilterBaseOptions nodeFilterOptions;

public Boolean call() throws IOException, InputError {
public Integer call() throws IOException, InputError {
AdhocResponse adhocResponse;

String project = getRdTool().projectOrEnv(options);
Expand Down Expand Up @@ -135,7 +135,7 @@ public Boolean call() throws IOException, InputError {
);
}

return Executions.maybeFollow(getRdTool(), followOptions, outputFormatOption, adhocResponse.execution.getId(), getRdOutput());
return Executions.maybeFollow(getRdTool(), followOptions, outputFormatOption, adhocResponse.execution.getId(), getRdOutput()) ? 0 : 1;
}

}
Loading