org.apache.hadoop.fs.s3a.AWSCredentialProviderList Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hadoop-aws Show documentation
Show all versions of hadoop-aws Show documentation
This module contains code to support integration with Amazon Web Services.
It also declares the dependencies needed to work with AWS services.
The newest version!
/*
* 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;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AnonymousAWSCredentials;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.fs.s3a.auth.NoAuthWithAWSException;
import org.apache.hadoop.io.IOUtils;
/**
* A list of providers.
*
* This is similar to the AWS SDK {@code AWSCredentialsProviderChain},
* except that:
*
* - Allows extra providers to be added dynamically.
* - If any provider in the chain throws an exception other than
* an {@link AmazonClientException}, that is rethrown, rather than
* swallowed.
* - Has some more diagnostics.
* - On failure, the last AmazonClientException raised is rethrown.
* - Special handling of {@link AnonymousAWSCredentials}.
*
*/
@InterfaceAudience.Private
@InterfaceStability.Evolving
public class AWSCredentialProviderList implements AWSCredentialsProvider,
AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(
AWSCredentialProviderList.class);
public static final String NO_AWS_CREDENTIAL_PROVIDERS
= "No AWS Credential Providers";
static final String
CREDENTIALS_REQUESTED_WHEN_CLOSED
= "Credentials requested after provider list was closed";
private final List providers = new ArrayList<>(1);
private boolean reuseLastProvider = true;
private AWSCredentialsProvider lastProvider;
private final AtomicInteger refCount = new AtomicInteger(1);
private final AtomicBoolean closed = new AtomicBoolean(false);
/**
* Empty instance. This is not ready to be used.
*/
public AWSCredentialProviderList() {
}
/**
* Create with an initial list of providers.
* @param providers provider list.
*/
public AWSCredentialProviderList(
Collection providers) {
this.providers.addAll(providers);
}
/**
* Add a new provider.
* @param p provider
*/
public void add(AWSCredentialsProvider p) {
providers.add(p);
}
/**
* Refresh all child entries.
*/
@Override
public void refresh() {
if (isClosed()) {
return;
}
for (AWSCredentialsProvider provider : providers) {
provider.refresh();
}
}
/**
* Iterate through the list of providers, to find one with credentials.
* If {@link #reuseLastProvider} is true, then it is re-used.
* @return a set of credentials (possibly anonymous), for authenticating.
*/
@Override
public AWSCredentials getCredentials() {
if (isClosed()) {
LOG.warn(CREDENTIALS_REQUESTED_WHEN_CLOSED);
throw new NoAuthWithAWSException(
CREDENTIALS_REQUESTED_WHEN_CLOSED);
}
checkNotEmpty();
if (reuseLastProvider && lastProvider != null) {
return lastProvider.getCredentials();
}
AmazonClientException lastException = null;
for (AWSCredentialsProvider provider : providers) {
try {
AWSCredentials credentials = provider.getCredentials();
if ((credentials.getAWSAccessKeyId() != null &&
credentials.getAWSSecretKey() != null)
|| (credentials instanceof AnonymousAWSCredentials)) {
lastProvider = provider;
LOG.debug("Using credentials from {}", provider);
return credentials;
}
} catch (AmazonClientException e) {
lastException = e;
LOG.debug("No credentials provided by {}: {}",
provider, e.toString(), e);
}
}
// no providers had any credentials. Rethrow the last exception
// or create a new one.
String message = "No AWS Credentials provided by "
+ listProviderNames();
if (lastException != null) {
message += ": " + lastException;
}
throw new NoAuthWithAWSException(message, lastException);
}
/**
* Returns the underlying list of providers.
*
* @return providers
*/
@VisibleForTesting
List getProviders() {
return providers;
}
/**
* Verify that the provider list is not empty.
* @throws AmazonClientException if there are no providers.
*/
public void checkNotEmpty() {
if (providers.isEmpty()) {
throw new NoAuthWithAWSException(NO_AWS_CREDENTIAL_PROVIDERS);
}
}
/**
* List all the providers' names.
* @return a list of names, separated by spaces (with a trailing one).
* If there are no providers, "" is returned.
*/
public String listProviderNames() {
return providers.stream()
.map(provider -> provider.getClass().getSimpleName() + ' ')
.collect(Collectors.joining());
}
/**
* The string value is this class name and the string values of nested
* providers.
* @return a string value for debugging.
*/
@Override
public String toString() {
return "AWSCredentialProviderList[" +
"refcount= " + refCount.get() + ": [" +
StringUtils.join(providers, ", ") + ']';
}
/**
* Get a reference to this object with an updated reference count.
*
* @return a reference to this
*/
public synchronized AWSCredentialProviderList share() {
Preconditions.checkState(!closed.get(), "Provider list is closed");
refCount.incrementAndGet();
return this;
}
/**
* Get the current reference count.
* @return the current ref count
*/
@VisibleForTesting
public int getRefCount() {
return refCount.get();
}
/**
* Get the closed flag.
* @return true iff the list is closed.
*/
@VisibleForTesting
public boolean isClosed() {
return closed.get();
}
/**
* Close routine will close all providers in the list which implement
* {@code Closeable}.
* This matters because some providers start a background thread to
* refresh their secrets.
*/
@Override
public void close() {
synchronized (this) {
if (closed.get()) {
// already closed: no-op
return;
}
int remainder = refCount.decrementAndGet();
if (remainder != 0) {
// still actively used, or somehow things are
// now negative
LOG.debug("Not closing {}", this);
return;
}
// at this point, the closing is going to happen
LOG.debug("Closing {}", this);
closed.set(true);
}
// do this outside the synchronized block.
for (AWSCredentialsProvider p : providers) {
if (p instanceof Closeable) {
IOUtils.closeStream((Closeable) p);
} else if (p instanceof AutoCloseable) {
S3AUtils.closeAutocloseables(LOG, (AutoCloseable)p);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy