blob: c316b91116fd5dac12287597087fd12267cf9bae [file] [log] [blame]
/*
* 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.hadoop.fs.s3a.auth;
import javax.annotation.Nullable;
import java.net.URI;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import com.amazonaws.SdkBaseException;
import com.amazonaws.auth.AWSCredentials;
import org.apache.hadoop.classification.VisibleForTesting;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.s3a.CredentialInitializationException;
import org.apache.hadoop.fs.s3a.Invoker;
import org.apache.hadoop.fs.s3a.Retries;
/**
* Base class for session credential support.
*
* @deprecated This class will be replaced by one that implements AWS SDK V2's AwsCredentialProvider
* as part of upgrading S3A to SDK V2. See HADOOP-18073.
*/
@InterfaceAudience.Private
@Deprecated
public abstract class AbstractSessionCredentialsProvider
extends AbstractAWSCredentialProvider {
/** Credentials, created in {@link #init()}. */
private AWSCredentials awsCredentials;
/** Atomic flag for on-demand initialization. */
private final AtomicBoolean initialized = new AtomicBoolean(false);
/**
* The (possibly translated) initialization exception.
* Used for testing.
*/
private IOException initializationException;
/**
* Constructor.
* @param uri possibly null filesystem URI.
* @param conf configuration.
*/
public AbstractSessionCredentialsProvider(
@Nullable final URI uri,
final Configuration conf) {
super(uri, conf);
}
/**
* Initialize the credentials by calling
* {@link #createCredentials(Configuration)} with the current config.
* @throws IOException on any failure.
*/
@Retries.OnceTranslated
protected void init() throws IOException {
// stop re-entrant attempts
if (initialized.getAndSet(true)) {
return;
}
try {
awsCredentials = Invoker.once("create credentials", "",
() -> createCredentials(getConf()));
} catch (IOException e) {
initializationException = e;
throw e;
}
}
/**
* Has an attempt to initialize the credentials been attempted?
* @return true if {@code init()} was called.
*/
public boolean isInitialized() {
return initialized.get();
}
/**
* Implementation point: whatever the subclass must do to load credentials.
* This is called from {@link #init()} and then the credentials are cached,
* along with any exception.
* @param config the configuration
* @return the credentials
* @throws IOException on any failure.
*/
protected abstract AWSCredentials createCredentials(Configuration config)
throws IOException;
/**
* Get the credentials.
* Any exception raised in
* {@link #createCredentials(Configuration)}
* is thrown here before any attempt to return the credentials
* is made.
* @return credentials, if set.
* @throws SdkBaseException if one was raised during init
* @throws CredentialInitializationException on other failures.
*/
public AWSCredentials getCredentials() throws SdkBaseException {
// do an on-demand init then raise an AWS SDK exception if
// there was a failure.
try {
if (!isInitialized()) {
init();
}
} catch (IOException e) {
if (e.getCause() instanceof SdkBaseException) {
throw (SdkBaseException) e.getCause();
} else {
throw new CredentialInitializationException(e.getMessage(), e);
}
}
if (awsCredentials == null) {
throw new CredentialInitializationException(
"Provider " + this + " has no credentials");
}
return awsCredentials;
}
public final boolean hasCredentials() {
return awsCredentials == null;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
/**
* Get any IOE raised during initialization.
* Null if {@link #init()} hasn't been called, or it actually worked.
* @return an exception or null.
*/
@VisibleForTesting
public IOException getInitializationException() {
return initializationException;
}
/**
* A special set of null credentials which are not the anonymous class.
* This will be interpreted as "this provider has no credentials to offer",
* rather than an explicit error or anonymous access.
*/
protected static final class NoCredentials implements AWSCredentials {
@Override
public String getAWSAccessKeyId() {
return null;
}
@Override
public String getAWSSecretKey() {
return null;
}
}
}