/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.accumulo.harness.conf;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.accumulo.cluster.ClusterUser;
import org.apache.accumulo.core.client.ClientConfiguration;
import org.apache.accumulo.core.client.ClientConfiguration.ClientProperty;
import org.apache.accumulo.core.client.Instance;
import org.apache.accumulo.core.client.ZooKeeperInstance;
import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
import org.apache.accumulo.core.client.security.tokens.KerberosToken;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.harness.AccumuloClusterHarness.ClusterType;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.UserGroupInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Extract connection information to a standalone Accumulo instance from Java properties
*/
public class StandaloneAccumuloClusterConfiguration extends AccumuloClusterPropertyConfiguration {
private static final Logger log = LoggerFactory.getLogger(StandaloneAccumuloClusterConfiguration.class);
public static final String ACCUMULO_STANDALONE_ADMIN_PRINCIPAL_KEY = ACCUMULO_STANDALONE_PREFIX + "admin.principal";
public static final String ACCUMULO_STANDALONE_ADMIN_PRINCIPAL_DEFAULT = "root";
public static final String ACCUMULO_STANDALONE_PASSWORD_KEY = ACCUMULO_STANDALONE_PREFIX + "admin.password";
public static final String ACCUMULO_STANDALONE_PASSWORD_DEFAULT = "rootPassword1";
public static final String ACCUMULO_STANDALONE_ADMIN_KEYTAB_KEY = ACCUMULO_STANDALONE_PREFIX + "admin.keytab";
public static final String ACCUMULO_STANDALONE_ZOOKEEPERS_KEY = ACCUMULO_STANDALONE_PREFIX + "zookeepers";
public static final String ACCUMULO_STANDALONE_ZOOKEEPERS_DEFAULT = "localhost";
public static final String ACCUMULO_STANDALONE_INSTANCE_NAME_KEY = ACCUMULO_STANDALONE_PREFIX + "instance.name";
public static final String ACCUMULO_STANDALONE_INSTANCE_NAME_DEFAULT = "accumulo";
public static final String ACCUMULO_STANDALONE_TMP_DIR_KEY = ACCUMULO_STANDALONE_PREFIX + "tmpdir";
public static final String ACCUMULO_STANDALONE_TMP_DIR_DEFAULT = "/tmp";
public static final String ACCUMULO_STANDALONE_SERVER_USER = ACCUMULO_STANDALONE_PREFIX + "server.user";
public static final String ACCUMULO_STANDALONE_SERVER_USER_DEFAULT = "accumulo";
// A set of users we can use to connect to this instances
public static final String ACCUMULO_STANDALONE_USER_KEY = ACCUMULO_STANDALONE_PREFIX + "users.";
// Keytabs for the users
public static final String ACCUMULO_STANDALONE_USER_KEYTABS_KEY = ACCUMULO_STANDALONE_PREFIX + "keytabs.";
// Passwords for the users
public static final String ACCUMULO_STANDALONE_USER_PASSWORDS_KEY = ACCUMULO_STANDALONE_PREFIX + "passwords.";
public static final String ACCUMULO_STANDALONE_HOME = ACCUMULO_STANDALONE_PREFIX + "home";
public static final String ACCUMULO_STANDALONE_CLIENT_CONF = ACCUMULO_STANDALONE_PREFIX + "client.conf";
public static final String ACCUMULO_STANDALONE_SERVER_CONF = ACCUMULO_STANDALONE_PREFIX + "server.conf";
public static final String ACCUMULO_STANDALONE_CLIENT_CMD_PREFIX = ACCUMULO_STANDALONE_PREFIX + "client.cmd.prefix";
public static final String ACCUMULO_STANDALONE_SERVER_CMD_PREFIX = ACCUMULO_STANDALONE_PREFIX + "server.cmd.prefix";
public static final String ACCUMULO_STANDALONE_HADOOP_CONF = ACCUMULO_STANDALONE_PREFIX + "hadoop.conf";
private Map<String,String> conf;
private String serverUser;
private File clientConfFile;
private ClientConfiguration clientConf;
private List<ClusterUser> clusterUsers;
public StandaloneAccumuloClusterConfiguration(File clientConfFile) {
ClusterType type = getClusterType();
if (ClusterType.STANDALONE != type) {
throw new IllegalStateException("Expected only to see standalone cluster state");
}
this.conf = getConfiguration(type);
this.clientConfFile = clientConfFile;
try {
this.clientConf = new ClientConfiguration(clientConfFile);
} catch (ConfigurationException e) {
throw new RuntimeException("Failed to load client configuration from " + clientConfFile);
}
// Update instance name if not already set
if (!clientConf.containsKey(ClientProperty.INSTANCE_NAME.getKey())) {
clientConf.withInstance(getInstanceName());
}
// Update zookeeper hosts if not already set
if (!clientConf.containsKey(ClientProperty.INSTANCE_ZK_HOST.getKey())) {
clientConf.withZkHosts(getZooKeepers());
}
// The user Accumulo is running as
serverUser = conf.get(ACCUMULO_STANDALONE_SERVER_USER);
if (null == serverUser) {
serverUser = ACCUMULO_STANDALONE_SERVER_USER_DEFAULT;
}
clusterUsers = new ArrayList<>();
for (Entry<String,String> entry : conf.entrySet()) {
String key = entry.getKey();
if (key.startsWith(ACCUMULO_STANDALONE_USER_KEY)) {
String suffix = key.substring(ACCUMULO_STANDALONE_USER_KEY.length());
String keytab = conf.get(ACCUMULO_STANDALONE_USER_KEYTABS_KEY + suffix);
if (null != keytab) {
File keytabFile = new File(keytab);
assertTrue("Keytab doesn't exist: " + keytabFile, keytabFile.exists() && keytabFile.isFile());
clusterUsers.add(new ClusterUser(entry.getValue(), keytabFile));
} else {
String password = conf.get(ACCUMULO_STANDALONE_USER_PASSWORDS_KEY + suffix);
if (null == password) {
throw new IllegalArgumentException("Missing password or keytab configuration for user with offset " + suffix);
}
clusterUsers.add(new ClusterUser(entry.getValue(), password));
}
}
}
log.info("Initialized Accumulo users with Kerberos keytabs: {}", clusterUsers);
}
@Override
public String getAdminPrincipal() {
String principal = conf.get(ACCUMULO_STANDALONE_ADMIN_PRINCIPAL_KEY);
if (null == principal) {
principal = ACCUMULO_STANDALONE_ADMIN_PRINCIPAL_DEFAULT;
}
return principal;
}
public String getPassword() {
String password = conf.get(ACCUMULO_STANDALONE_PASSWORD_KEY);
if (null == password) {
password = ACCUMULO_STANDALONE_PASSWORD_DEFAULT;
}
return password;
}
public File getAdminKeytab() {
String keytabPath = conf.get(ACCUMULO_STANDALONE_ADMIN_KEYTAB_KEY);
if (null == keytabPath) {
throw new RuntimeException("SASL is enabled, but " + ACCUMULO_STANDALONE_ADMIN_KEYTAB_KEY + " was not provided");
}
File keytab = new File(keytabPath);
if (!keytab.exists() || !keytab.isFile()) {
throw new RuntimeException(keytabPath + " should be a regular file");
}
return keytab;
}
@Override
public AuthenticationToken getAdminToken() {
if (clientConf.getBoolean(ClientProperty.INSTANCE_RPC_SASL_ENABLED.getKey(), false)) {
File keytab = getAdminKeytab();
try {
UserGroupInformation.loginUserFromKeytab(getAdminPrincipal(), keytab.getAbsolutePath());
return new KerberosToken();
} catch (IOException e) {
// The user isn't logged in
throw new RuntimeException("Failed to create KerberosToken", e);
}
} else {
return new PasswordToken(getPassword());
}
}
public String getZooKeepers() {
if (clientConf.containsKey(ClientProperty.INSTANCE_ZK_HOST.getKey())) {
return clientConf.get(ClientProperty.INSTANCE_ZK_HOST);
}
String zookeepers = conf.get(ACCUMULO_STANDALONE_ZOOKEEPERS_KEY);
if (null == zookeepers) {
zookeepers = ACCUMULO_STANDALONE_ZOOKEEPERS_DEFAULT;
}
return zookeepers;
}
public String getInstanceName() {
if (clientConf.containsKey(ClientProperty.INSTANCE_NAME.getKey())) {
return clientConf.get(ClientProperty.INSTANCE_NAME);
}
String instanceName = conf.get(ACCUMULO_STANDALONE_INSTANCE_NAME_KEY);
if (null == instanceName) {
instanceName = ACCUMULO_STANDALONE_INSTANCE_NAME_DEFAULT;
}
return instanceName;
}
public Instance getInstance() {
// Make sure the ZKI is created with the ClientConf so it gets things like SASL passed through to the connector
return new ZooKeeperInstance(clientConf);
}
@Override
public ClusterType getClusterType() {
return ClusterType.STANDALONE;
}
public String getHadoopConfDir() {
return conf.get(ACCUMULO_STANDALONE_HADOOP_CONF);
}
public String getAccumuloHome() {
return conf.get(ACCUMULO_STANDALONE_HOME);
}
public String getClientAccumuloConfDir() {
return conf.get(ACCUMULO_STANDALONE_CLIENT_CONF);
}
public String getServerAccumuloConfDir() {
return conf.get(ACCUMULO_STANDALONE_SERVER_CONF);
}
public String getServerCmdPrefix() {
return conf.get(ACCUMULO_STANDALONE_SERVER_CMD_PREFIX);
}
public String getClientCmdPrefix() {
return conf.get(ACCUMULO_STANDALONE_CLIENT_CMD_PREFIX);
}
@Override
public ClientConfiguration getClientConf() {
return clientConf;
}
public File getClientConfFile() {
return clientConfFile;
}
public Path getTmpDirectory() {
String tmpDir = conf.get(ACCUMULO_STANDALONE_TMP_DIR_KEY);
if (null == tmpDir) {
tmpDir = ACCUMULO_STANDALONE_TMP_DIR_DEFAULT;
}
return new Path(tmpDir);
}
public List<ClusterUser> getUsers() {
return Collections.unmodifiableList(clusterUsers);
}
/**
* @return The user Accumulo is running as
*/
public String getAccumuloServerUser() {
return serverUser;
}
}