blob: 3de0bf105ec8ccffbcfa4955eb4c8e5785d539e6 [file] [log] [blame]
package org.jclouds.scriptbuilder.statements.login;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import org.jclouds.javax.annotation.Nullable;
import clojure.main;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.cache.CacheBuilderSpec;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
/**
* A specification of a {@link AdminAccess.Builder} configuration.
*
* <p>
* {@code AdminAccess.Builder} supports parsing configuration off of a string, which makes it
* especially useful for command-line configuration of a {@code AdminAccess.Builder}.
*
* <p>
* The string syntax is a series of comma-separated keys or key-value pairs, each corresponding to a
* {@code AdminAccess.Builder} method.
* <ul>
* <li>{@code adminUsername=[String]}: sets {@link AdminAccess.Builder#adminUsername}.
* <li>{@code adminHome=[String]}: sets {@link AdminAccess.Builder#adminHome}.
* <li>{@code adminPublicKeyFile=[String]}: sets {@link AdminAccess.Builder#adminPublicKeyFile}.
* <li>{@code adminPrivateKeyFile=[String]}: sets {@link AdminAccess.Builder#adminPrivateKeyFile}. *
* <li>{@code adminPassword=[String]}: sets {@link AdminAccess.Builder#adminPassword}.
* <li>{@code loginPassword=[String]}: sets {@link AdminAccess.Builder#loginPassword}.
* <li>{@code lockSsh=[Boolean]}: sets {@link TemplateBuilder#lockSsh}.
* <li>{@code grantSudoToAdminUser=[Boolean]}: sets {@link TemplateBuilder#grantSudoToAdminUser}.
* <li>{@code authorizeAdminPublicKey=[Boolean]}: sets
* {@link TemplateBuilder#authorizeAdminPublicKey}.
* <li>{@code installAdminPrivateKey=[Boolean]}: sets {@link TemplateBuilder#installAdminPrivateKey}.
* <li>{@code resetLoginPassword=[Boolean]}: sets {@link TemplateBuilder#resetLoginPassword}.
* </ul>
*
* <p>
* Whitespace before and after commas and equal signs is ignored. Keys may not be repeated and both
* private key and public key must be passed through files, as they might include weird characters.
*
* <p>
* {@code AdminAccessBuilderSpec} does not support configuring {@code AdminAccess.Builder} methods
* with non-value parameters. These must be configured in code.
*
* <p>
* A new {@code AdminAccess.Builder} can be instantiated from a {@code AdminAccessBuilderSpec} using
* {@link AdminAccess.Builder#from(AdminAccessBuilderSpec)} or
* {@link AdminAccess.Builder#from(String)}.
*
* <p>
* Design inspired by {@link CacheBuilderSpec}
*
* @author David Alves
* @since 1.5
*/
public class AdminAccessBuilderSpec {
private static final long serialVersionUID = -379469670373111569L;
/** Parses a single value. */
protected static interface ValueParser {
void parse(AdminAccessBuilderSpec spec, String key, @Nullable String value);
}
/** Splits each key-value pair. */
protected static final Splitter KEYS_SPLITTER = Splitter.on(',').trimResults();
/** Splits the key from the value. */
protected static final Splitter KEY_VALUE_SPLITTER = Splitter.on('=').trimResults();
/** Map of names to ValueParser. */
protected static final ImmutableMap<String, ValueParser> VALUE_PARSERS = ImmutableMap
.<String, ValueParser> builder().put("adminUsername", new AdminUserNameParser())
.put("adminHome", new AdminHomeParser()).put("adminPublicKeyFile", new AdminPublicKeyFileParser())
.put("adminPrivateKeyFile", new AdminPrivateKeyFileParser())
.put("adminPassword", new AdminPasswordParser()).put("loginPassword", new LoginPasswordParser())
.put("lockSsh", new LockSshParser()).put("grantSudoToAdminUser", new GrantSudoToAdminUserParser())
.put("authorizeAdminPublicKey", new AuthorizeAdminPublicKeyParser())
.put("installAdminPrivateKey", new InstallAdminPrivateKeyParser())
.put("resetLoginPassword", new ResetLoginPasswordParser()).build();
@VisibleForTesting
String adminUsername;
@VisibleForTesting
String adminHome;
@VisibleForTesting
File adminPublicKeyFile;
@VisibleForTesting
File adminPrivateKeyFile;
@VisibleForTesting
String adminPassword;
@VisibleForTesting
String loginPassword;
@VisibleForTesting
Boolean lockSsh;
@VisibleForTesting
Boolean grantSudoToAdminUser;
@VisibleForTesting
Boolean authorizeAdminPublicKey;
@VisibleForTesting
Boolean installAdminPrivateKey;
@VisibleForTesting
Boolean resetLoginPassword;
/** Base class for parsing strings. */
abstract static class StringParser implements ValueParser {
protected abstract void set(AdminAccessBuilderSpec spec, String value);
@Override
public void parse(AdminAccessBuilderSpec spec, String key, String value) {
checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
set(spec, value);
}
}
/** Base class for parsing booleans. */
abstract static class BooleanParser implements ValueParser {
protected abstract void parseBoolean(AdminAccessBuilderSpec spec, boolean value);
@Override
public void parse(AdminAccessBuilderSpec spec, String key, String value) {
checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
try {
parseBoolean(spec, Boolean.parseBoolean(value));
} catch (NumberFormatException e) {
throw new IllegalArgumentException(String.format("key %s value set to %s, must be booleans", key, value), e);
}
}
}
/** Base class for parsing files. */
abstract static class FileParser implements ValueParser {
protected abstract void set(AdminAccessBuilderSpec spec, File value);
@Override
public void parse(AdminAccessBuilderSpec spec, String key, String value) {
checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
File file = new File(value);
if (!file.exists()) {
throw new IllegalArgumentException(String.format("key %s value set to %s, must be an existing file", key,
value));
}
set(spec, file);
}
}
/** Parse adminUsername */
static class AdminUserNameParser extends StringParser {
@Override
protected void set(AdminAccessBuilderSpec spec, String value) {
checkArgument(spec.adminUsername == null, "admin username was already set to ", spec.adminUsername);
spec.adminUsername = value;
}
}
/** Parse adminHome */
static class AdminHomeParser extends StringParser {
@Override
protected void set(AdminAccessBuilderSpec spec, String value) {
checkArgument(spec.adminHome == null, "admin home was already set to ", spec.adminHome);
spec.adminHome = value;
}
}
/** Parse adminPublicKeyFile */
static class AdminPublicKeyFileParser extends FileParser {
@Override
protected void set(AdminAccessBuilderSpec spec, File value) {
checkArgument(spec.adminPublicKeyFile == null, "admin public key file was already set to ",
spec.adminPublicKeyFile);
spec.adminPublicKeyFile = value;
}
}
/** Parse adminPrivateKeyFile */
static class AdminPrivateKeyFileParser extends FileParser {
@Override
protected void set(AdminAccessBuilderSpec spec, File value) {
checkArgument(spec.adminPrivateKeyFile == null, "admin private key file was already set to ",
spec.adminPrivateKeyFile);
spec.adminPrivateKeyFile = value;
}
}
/** Parse adminPassword */
static class AdminPasswordParser extends StringParser {
@Override
protected void set(AdminAccessBuilderSpec spec, String value) {
checkArgument(spec.adminPassword == null, "admin password was already set to ", spec.adminPassword);
spec.adminPassword = value;
}
}
/** Parse loginPassword */
static class LoginPasswordParser extends StringParser {
@Override
protected void set(AdminAccessBuilderSpec spec, String value) {
checkArgument(spec.loginPassword == null, "login password was already set to ", spec.loginPassword);
spec.loginPassword = value;
}
}
/** Parse lockSsh */
static class LockSshParser extends BooleanParser {
@Override
protected void parseBoolean(AdminAccessBuilderSpec spec, boolean value) {
checkArgument(spec.loginPassword == null, "lockSsh was already set to ", spec.lockSsh);
spec.lockSsh = value;
}
}
/** Parse grantSudoToAdminUser */
static class GrantSudoToAdminUserParser extends BooleanParser {
@Override
protected void parseBoolean(AdminAccessBuilderSpec spec, boolean value) {
checkArgument(spec.grantSudoToAdminUser == null, "grant sudo to admin user was already set to ",
spec.grantSudoToAdminUser);
spec.grantSudoToAdminUser = value;
}
}
/** Parse authorizeAdminPublickKey */
static class AuthorizeAdminPublicKeyParser extends BooleanParser {
@Override
protected void parseBoolean(AdminAccessBuilderSpec spec, boolean value) {
checkArgument(spec.authorizeAdminPublicKey == null, "authorize admin public key was already set to ",
spec.authorizeAdminPublicKey);
spec.authorizeAdminPublicKey = value;
}
}
/** Parse installPrivateKey */
static class InstallAdminPrivateKeyParser extends BooleanParser {
@Override
protected void parseBoolean(AdminAccessBuilderSpec spec, boolean value) {
checkArgument(spec.installAdminPrivateKey == null, "install admin private key was already set to ",
spec.installAdminPrivateKey);
spec.installAdminPrivateKey = value;
}
}
/** Parse resetLoginPassword */
static class ResetLoginPasswordParser extends BooleanParser {
@Override
protected void parseBoolean(AdminAccessBuilderSpec spec, boolean value) {
checkArgument(spec.resetLoginPassword == null, "reset login password was already set to ",
spec.resetLoginPassword);
spec.resetLoginPassword = value;
}
}
/** Specification; used for toParseableString(). */
// transient in case people using serializers don't want this to show up
protected transient String[] specifications;
protected AdminAccessBuilderSpec() {
// we want serializers like Gson to work w/o using sun.misc.Unsafe,
// prohibited in GAE. This also implies fields are not final.
// see http://code.google.com/p/jclouds/issues/detail?spec=925
}
protected AdminAccessBuilderSpec(String... specifications) {
this.specifications = specifications;
}
/**
* Creates a AdminAccessBuilderSpec from multiple specifications,
*
* @param adminAccessSpecification
* the string form
*/
public static AdminAccessBuilderSpec parse(String adminAccessSpecification) {
AdminAccessBuilderSpec spec = new AdminAccessBuilderSpec(adminAccessSpecification);
if (!adminAccessSpecification.isEmpty()) {
for (String keyValuePair : KEYS_SPLITTER.split(adminAccessSpecification)) {
List<String> keyAndValue = ImmutableList.copyOf(KEY_VALUE_SPLITTER.split(keyValuePair));
checkArgument(!keyAndValue.isEmpty(), "blank key-value pair");
checkArgument(keyAndValue.size() <= 2, "key-value pair %s with more than one equals sign", keyValuePair);
// Find the ValueParser for the current key.
String key = keyAndValue.get(0);
ValueParser valueParser = VALUE_PARSERS.get(key);
checkArgument(valueParser != null, "unknown key %s", key);
String value = keyAndValue.size() == 1 ? null : keyAndValue.get(1);
valueParser.parse(spec, key, value);
}
}
return spec;
}
/**
* Returns a AdminAccess.Builder configured according to this instance's specification.
*/
public AdminAccess.Builder copyTo(AdminAccess.Builder builder) {
if (adminUsername != null) {
builder.adminUsername(adminUsername);
}
if (adminHome != null) {
builder.adminHome(adminHome);
}
if (adminPublicKeyFile != null) {
builder.adminPublicKey(adminPublicKeyFile);
}
if (adminPrivateKeyFile != null) {
builder.adminPrivateKey(adminPrivateKeyFile);
}
if (adminPassword != null) {
builder.adminPassword(adminPassword);
}
if (loginPassword != null) {
builder.loginPassword(loginPassword);
}
if (lockSsh != null) {
builder.lockSsh(lockSsh);
}
if (grantSudoToAdminUser != null) {
builder.grantSudoToAdminUser(grantSudoToAdminUser);
}
if (authorizeAdminPublicKey != null) {
builder.authorizeAdminPublicKey(authorizeAdminPublicKey);
}
if (installAdminPrivateKey != null) {
builder.installAdminPrivateKey(installAdminPrivateKey);
}
if (resetLoginPassword != null) {
builder.resetLoginPassword(resetLoginPassword);
}
return builder;
}
/**
* Returns a string that can be used to parse an equivalent {@code AdminAccessSpec}. The order
* and form of this representation is not guaranteed, except that reparsing its output will
* produce a {@code AdminAccessSpec} equal to this instance.
*/
public String[] toParsableStrings() {
return specifications;
}
/**
* Returns a string representation for this AdminAccessSpec instance. The form of this
* representation is not guaranteed.
*/
@Override
public String toString() {
return toStringHelper(this).addValue(Arrays.toString(toParsableStrings())).toString();
}
@Override
public int hashCode() {
return Objects.hashCode(adminUsername, adminHome, adminPublicKeyFile, adminPrivateKeyFile, adminPassword,
loginPassword, lockSsh, grantSudoToAdminUser, authorizeAdminPublicKey, installAdminPrivateKey,
resetLoginPassword);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AdminAccessBuilderSpec)) {
return false;
}
AdminAccessBuilderSpec that = (AdminAccessBuilderSpec) obj;
return equal(adminUsername, that.adminUsername) && equal(adminHome, that.adminHome)
&& equal(adminPublicKeyFile, that.adminPublicKeyFile)
&& equal(adminPrivateKeyFile, that.adminPrivateKeyFile) && equal(adminPassword, that.adminPassword)
&& equal(loginPassword, that.loginPassword) && equal(lockSsh, that.lockSsh)
&& equal(grantSudoToAdminUser, that.grantSudoToAdminUser)
&& equal(installAdminPrivateKey, that.installAdminPrivateKey)
&& equal(resetLoginPassword, that.resetLoginPassword);
}
public String getAdminUsername() {
return adminUsername;
}
public String getAdminHome() {
return adminHome;
}
public File getAdminPublicKeyFile() {
return adminPublicKeyFile;
}
public File getAdminPrivateKeyFile() {
return adminPrivateKeyFile;
}
public String getAdminPassword() {
return adminPassword;
}
public String getLoginPassword() {
return loginPassword;
}
public Boolean getLockSsh() {
return lockSsh;
}
public Boolean getGrantSudoToAdminUser() {
return grantSudoToAdminUser;
}
public Boolean getInstallAdminPrivateKey() {
return installAdminPrivateKey;
}
public Boolean getResetLoginPassword() {
return resetLoginPassword;
}
}