All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.fs.s3a.AWSCredentialProviderList Maven / Gradle / Ivy

/*
 * 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:
 * 
    *
  1. Allows extra providers to be added dynamically.
  2. *
  3. If any provider in the chain throws an exception other than * an {@link AmazonClientException}, that is rethrown, rather than * swallowed.
  4. *
  5. Has some more diagnostics.
  6. *
  7. On failure, the last AmazonClientException raised is rethrown.
  8. *
  9. Special handling of {@link AnonymousAWSCredentials}.
  10. *
*/ @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