org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstance.java Source code

Java tutorial

Introduction

Here is the source code for org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstance.java

Source

/*
 * 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.jclouds.ec2.compute.functions;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.jclouds.util.Predicates2.retry;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.EC2Api;
import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.domain.PasswordData;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.logging.Logger;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.Atomics;

/**
 * @author Adrian Cole
 */
@Singleton
public class PasswordCredentialsFromWindowsInstance implements Function<RunningInstance, LoginCredentials> {

    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;

    private final ConcurrentMap<RegionAndName, KeyPair> credentialsMap;
    private final EC2Api ec2Api;
    private final Function<PasswordDataAndPrivateKey, LoginCredentials> pwDataToLoginCredentials;

    @Inject
    protected PasswordCredentialsFromWindowsInstance(ConcurrentMap<RegionAndName, KeyPair> credentialsMap,
            EC2Api ec2Api, Function<PasswordDataAndPrivateKey, LoginCredentials> pwDataToLoginCredentials) {
        this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap");
        this.ec2Api = checkNotNull(ec2Api, "ec2Api");
        this.pwDataToLoginCredentials = checkNotNull(pwDataToLoginCredentials, "pwDataToLoginCredentials");
    }

    @Override
    public LoginCredentials apply(final RunningInstance instance) {
        Optional<? extends WindowsApi> windowsOption = ec2Api.getWindowsApiForRegion(instance.getRegion());
        checkState(windowsOption.isPresent(), "windows feature not present in region %s", instance.getRegion());

        final WindowsApi windowsApi = windowsOption.get();

        LoginCredentials credentials = LoginCredentials.builder().user("Administrator").noPrivateKey().build();
        String privateKey = getPrivateKeyOrNull(instance);
        if (privateKey == null) {
            return credentials;
        }
        // The Administrator password will take some time before it is ready - Amazon says
        // sometimes
        // 15 minutes.
        // So we create a predicate that tests if the password is ready, and wrap it in a retryable
        // predicate.
        final AtomicReference<PasswordData> data = Atomics.newReference();
        Predicate<String> passwordReady = new Predicate<String>() {
            @Override
            public boolean apply(@Nullable String s) {
                if (Strings.isNullOrEmpty(s))
                    return false;
                data.set(windowsApi.getPasswordDataForInstance(instance.getId()));
                if (data.get() == null)
                    return false;
                return !Strings.isNullOrEmpty(data.get().getPasswordData());
            }
        };

        // TODO: parameterize
        Predicate<String> passwordReadyRetryable = retry(passwordReady, 600, 10, SECONDS);

        logger.debug(">> awaiting password data for instance(%s/%s)", instance.getRegion(), instance.getId());
        if (passwordReadyRetryable.apply(instance.getId())) {
            credentials = pwDataToLoginCredentials.apply(new PasswordDataAndPrivateKey(data.get(), privateKey));
            logger.debug("<< obtained password data for instance(%s/%s)", instance.getRegion(), instance.getId());
        } else {
            logger.debug("<< unable to get password data for instance(%s/%s)", instance.getRegion(),
                    instance.getId());
        }
        return credentials;
    }

    @VisibleForTesting
    String getPrivateKeyOrNull(RunningInstance instance) {
        KeyPair keyPair = credentialsMap.get(new RegionAndName(instance.getRegion(), instance.getKeyName()));
        return keyPair != null ? keyPair.getKeyMaterial() : null;
    }

}