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

Node source in the runner #336

Merged
merged 5 commits into from
Aug 21, 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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ configurations {

dependencies {
pluginLibs 'com.google.code.gson:gson:2.10.1'
implementation('org.rundeck:rundeck-core:4.14.0-rc1-20230606')
implementation('org.rundeck:rundeck-core:4.16.0-rc1-20230815')
implementation 'org.codehaus.groovy:groovy-all:3.0.9'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package com.rundeck.plugins.ansible.plugin;

import com.dtolabs.rundeck.core.execution.proxy.DefaultSecretBundle;
import com.dtolabs.rundeck.core.execution.proxy.ProxySecretBundleCreator;
import com.dtolabs.rundeck.core.execution.proxy.SecretBundle;
import com.dtolabs.rundeck.core.storage.ResourceMeta;
import com.dtolabs.rundeck.core.storage.StorageTree;
import com.dtolabs.rundeck.core.storage.keys.KeyStorageTree;
import com.rundeck.plugins.ansible.ansible.AnsibleDescribable;
import com.rundeck.plugins.ansible.ansible.AnsibleDescribable.AuthenticationType;
import com.rundeck.plugins.ansible.ansible.AnsibleException;
Expand All @@ -18,8 +24,12 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import org.rundeck.app.spi.Services;
import org.rundeck.storage.api.PathUtil;
import org.rundeck.storage.api.StorageException;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
Expand All @@ -28,10 +38,12 @@
import java.util.*;
import java.util.Map.Entry;

public class AnsibleResourceModelSource implements ResourceModelSource {
public class AnsibleResourceModelSource implements ResourceModelSource, ProxySecretBundleCreator {

private Framework framework;

private Services services;

private String project;
private String sshAuthType;

Expand All @@ -55,6 +67,9 @@ public class AnsibleResourceModelSource implements ResourceModelSource {
protected Boolean sshUsePassword;
protected String sshPassword;
protected String sshPrivateKeyFile;

protected String sshPasswordPath;
protected String sshPrivateKeyPath;
protected String sshPass;
protected Integer sshTimeout;

Expand All @@ -68,12 +83,20 @@ public class AnsibleResourceModelSource implements ResourceModelSource {
protected String vaultFile;
protected String vaultPassword;

protected String vaultPasswordPath;

protected String baseDirectoryPath;

protected String ansibleBinariesDirectoryPath;

protected String extraParameters;

protected String sshAgent;
protected String sshPassphraseStoragePath;

protected String becamePasswordStoragePath;


public AnsibleResourceModelSource(final Framework framework) {
this.framework = framework;
}
Expand All @@ -99,6 +122,10 @@ private static Boolean skipVar(final String hostVar, final List<String> varList)
return false;
}

public void setServices(Services services) {
this.services = services;
}

public void configure(Properties configuration) throws ConfigurationException {

project = configuration.getProperty("project");
Expand Down Expand Up @@ -156,6 +183,16 @@ public void configure(Properties configuration) throws ConfigurationException {

extraParameters = (String) resolveProperty(AnsibleDescribable.ANSIBLE_EXTRA_PARAM,null,configuration,executionDataContext);

sshPasswordPath = (String) resolveProperty(AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH,null,configuration,executionDataContext);
sshPrivateKeyPath = (String) resolveProperty(AnsibleDescribable.ANSIBLE_SSH_KEYPATH_STORAGE_PATH,null,configuration,executionDataContext);

vaultPasswordPath = (String) resolveProperty(AnsibleDescribable.ANSIBLE_VAULTSTORE_PATH,null,configuration,executionDataContext);

sshAgent = (String) resolveProperty(AnsibleDescribable.ANSIBLE_SSH_USE_AGENT,null,configuration,executionDataContext);
sshPassphraseStoragePath = (String) resolveProperty(AnsibleDescribable.ANSIBLE_SSH_PASSPHRASE,null,configuration,executionDataContext);
vaultPasswordPath = (String) resolveProperty(AnsibleDescribable.ANSIBLE_BECOME_PASSWORD_STORAGE_PATH,null,configuration,executionDataContext);

becamePasswordStoragePath = (String) resolveProperty(AnsibleDescribable.ANSIBLE_BECOME_PASSWORD_STORAGE_PATH,null,configuration,executionDataContext);
}

public AnsibleRunner buildAnsibleRunner() throws ResourceModelSourceException{
Expand All @@ -172,6 +209,9 @@ public AnsibleRunner buildAnsibleRunner() throws ResourceModelSourceException{
runner.limit(limitList);
}

StorageTree storageTree = services.getService(KeyStorageTree.class);


if ( sshAuthType.equalsIgnoreCase(AuthenticationType.privateKey.name()) ) {
if (sshPrivateKeyFile != null) {
String sshPrivateKey;
Expand All @@ -182,12 +222,43 @@ public AnsibleRunner buildAnsibleRunner() throws ResourceModelSourceException{
}
runner = runner.sshPrivateKey(sshPrivateKey);
}

if(sshPrivateKeyPath !=null && !sshPrivateKeyPath.isEmpty()){
try {
String sshPrivateKey = getStorageContentString(sshPrivateKeyPath, storageTree);
runner = runner.sshPrivateKey(sshPrivateKey);
} catch (ConfigurationException e) {
throw new ResourceModelSourceException("Could not read password from storage path " + sshPasswordPath,e);
}
}

if(sshAgent != null && sshAgent.equalsIgnoreCase("true")) {
runner = runner.sshUseAgent(Boolean.TRUE);

if(sshPassphraseStoragePath != null && !sshPassphraseStoragePath.isEmpty()) {
try {
String sshPassphrase = getStorageContentString(sshPassphraseStoragePath, storageTree);
runner = runner.sshPassphrase(sshPassphrase);
} catch (ConfigurationException e) {
throw new ResourceModelSourceException("Could not read passphrase from storage path " + sshPassphraseStoragePath,e);
}
}
}

} else if ( sshAuthType.equalsIgnoreCase(AuthenticationType.password.name()) ) {
if (sshPassword != null) {
runner = runner.sshUsePassword(Boolean.TRUE).sshPass(sshPassword);
}
}

if(sshPasswordPath !=null && !sshPasswordPath.isEmpty()){
try {
sshPassword = getStorageContentString(sshPasswordPath, storageTree);
runner = runner.sshUsePassword(Boolean.TRUE).sshPass(sshPassword);
} catch (ConfigurationException e) {
throw new ResourceModelSourceException("Could not read password from storage path " + sshPasswordPath,e);
}
}
}

if (inventory != null) {
runner = runner.setInventory(inventory);
Expand Down Expand Up @@ -220,34 +291,55 @@ public AnsibleRunner buildAnsibleRunner() throws ResourceModelSourceException{
runner = runner.becomePassword(becomePassword);
}

if (configFile != null) {
runner = runner.configFile(configFile);
if(becamePasswordStoragePath != null && !becamePasswordStoragePath.isEmpty()){
try {
becomePassword = getStorageContentString(becamePasswordStoragePath, storageTree);
runner = runner.becomePassword(becomePassword);
} catch (Exception e) {
throw new ResourceModelSourceException("Could not read becomePassword from storage path " + becamePasswordStoragePath,e);
}
}

if (configFile != null) {
runner = runner.configFile(configFile);
}

if(vaultPassword!=null) {
if(vaultPassword!=null) {
runner.vaultPass(vaultPassword);
}
}

if (vaultFile != null) {
String vaultPassword;
try {
vaultPassword = new String(Files.readAllBytes(Paths.get(vaultFile)));
} catch (IOException e) {
throw new ResourceModelSourceException("Could not read vault file " + vaultFile,e);
}
runner.vaultPass(vaultPassword);
}
if (baseDirectoryPath != null) {
runner.baseDirectory(baseDirectoryPath);
if(vaultPasswordPath!=null && !vaultPasswordPath.isEmpty()){
try {
vaultPassword = getStorageContentString(vaultPasswordPath, storageTree);
} catch (Exception e) {
throw new ResourceModelSourceException("Could not read vaultPassword " + vaultPasswordPath,e);
}
runner = runner.vaultPass(vaultPassword);
}

if (ansibleBinariesDirectoryPath != null) {
runner.ansibleBinariesDirectory(ansibleBinariesDirectoryPath);
if (vaultFile != null) {
String vaultPassword;
try {
vaultPassword = new String(Files.readAllBytes(Paths.get(vaultFile)));
} catch (IOException e) {
throw new ResourceModelSourceException("Could not read vault file " + vaultFile,e);
}
runner.vaultPass(vaultPassword);
}
if (baseDirectoryPath != null) {
runner.baseDirectory(baseDirectoryPath);
}

if (ansibleBinariesDirectoryPath != null) {
runner.ansibleBinariesDirectory(ansibleBinariesDirectoryPath);
}

if (extraParameters != null){
runner.extraParams(extraParameters);
}



if (extraParameters != null){
runner.extraParams(extraParameters);
}

return runner;
}
Expand Down Expand Up @@ -534,4 +626,126 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx

return nodes;
}



private String getStorageContentString(String storagePath, StorageTree storageTree) throws ConfigurationException {
return new String(this.getStorageContent(storagePath, storageTree));
}

private byte[] getStorageContent(String storagePath, StorageTree storageTree) throws ConfigurationException {
org.rundeck.storage.api.Path path = PathUtil.asPath(storagePath);
try {
ResourceMeta contents = storageTree.getResource(path).getContents();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
contents.writeContent(byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} catch (StorageException e) {
throw new ConfigurationException("Failed to read the ssh private key for " +
"storage path: " + storagePath + ": " + e.getMessage());
} catch (IOException e) {
throw new ConfigurationException("Failed to read the ssh private key for " +
"storage path: " + storagePath + ": " + e.getMessage());
}
}


@Override
public List<String> listSecretsPathResourceModel(Map<String, Object> configuration){
List<String> keys = new ArrayList<>();

String passwordStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH);
String privateKeyStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_KEYPATH_STORAGE_PATH);
String passphraseStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_PASSPHRASE);
String vaultPasswordStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_VAULTSTORE_PATH);
String becamePasswordStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_BECOME_PASSWORD_STORAGE_PATH);

if(passwordStoragePath!=null && !passwordStoragePath.isEmpty()){
keys.add(passwordStoragePath);
}

if(privateKeyStoragePath!=null && !privateKeyStoragePath.isEmpty()){
if(!keys.contains(privateKeyStoragePath)){
keys.add(privateKeyStoragePath);
}
}

if(passphraseStoragePath!=null && !passphraseStoragePath.isEmpty()){
if(!keys.contains(passphraseStoragePath)){
keys.add(passphraseStoragePath);
}
}

if(vaultPasswordStoragePath!=null && !vaultPasswordStoragePath.isEmpty()){
if(!keys.contains(vaultPasswordStoragePath)){
keys.add(vaultPasswordStoragePath);
}
}

if(becamePasswordStoragePath!=null && !becamePasswordStoragePath.isEmpty()){
if(!keys.contains(becamePasswordStoragePath)){
keys.add(becamePasswordStoragePath);
}
}

return keys;

}

@Override
public SecretBundle prepareSecretBundleResourceModel(Services services, Map<String, Object> configuration){
DefaultSecretBundle secretBundle = new DefaultSecretBundle();

try {
StorageTree storageTree = services.getService(KeyStorageTree.class);

String passwordStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH);
String privateKeyStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_KEYPATH_STORAGE_PATH);
String passphraseStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_PASSPHRASE);
String vaultPasswordStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_VAULTSTORE_PATH);
String becamePasswordStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_BECOME_PASSWORD_STORAGE_PATH);

if(passwordStoragePath!=null && !passwordStoragePath.isEmpty()){
secretBundle.addSecret(
passwordStoragePath,
getStorageContent(passwordStoragePath,storageTree )
);
}

if(privateKeyStoragePath!=null && !privateKeyStoragePath.isEmpty()){
secretBundle.addSecret(
privateKeyStoragePath,
getStorageContent(privateKeyStoragePath,storageTree )
);
}

if(passphraseStoragePath!=null && !passphraseStoragePath.isEmpty()){
secretBundle.addSecret(
passphraseStoragePath,
getStorageContent(passphraseStoragePath,storageTree )
);
}

if(vaultPasswordStoragePath!=null && !vaultPasswordStoragePath.isEmpty()){
secretBundle.addSecret(
vaultPasswordStoragePath,
getStorageContent(vaultPasswordStoragePath,storageTree )
);
}

if(becamePasswordStoragePath!=null && !becamePasswordStoragePath.isEmpty()){
secretBundle.addSecret(
becamePasswordStoragePath,
getStorageContent(becamePasswordStoragePath,storageTree )
);
}

return secretBundle;

} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}


}
Loading
Loading