Package org.apache.whirr.service

Source Code of org.apache.whirr.service.IgnoreCloseComputeServiceContext

/**
* 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.whirr.service;

import static com.google.common.base.Preconditions.checkArgument;
import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_AMI_QUERY;
import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY;
import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION;

import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationConverter;
import org.apache.commons.lang.StringUtils;
import org.apache.whirr.ClusterSpec;
import org.jclouds.Context;
import org.jclouds.ContextBuilder;
import org.jclouds.apis.ApiMetadata;
import org.jclouds.apis.Apis;
import org.jclouds.byon.BYONApiMetadata;
import org.jclouds.byon.Node;
import org.jclouds.byon.config.BYONComputeServiceContextModule;
import org.jclouds.byon.config.CacheNodeStoreModule;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.Utils;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.events.StatementOnNodeCompletion;
import org.jclouds.compute.events.StatementOnNodeFailure;
import org.jclouds.compute.events.StatementOnNodeSubmission;
import org.jclouds.domain.Credentials;
import org.jclouds.enterprise.config.EnterpriseConfigurationModule;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.jclouds.providers.ProviderMetadata;
import org.jclouds.providers.Providers;
import org.jclouds.rest.RestContext;
import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ForwardingObject;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.google.common.reflect.TypeToken;
import com.google.inject.Module;

/**
* A convenience class for building jclouds {@link ComputeServiceContext} objects.
*/
// singleton enum pattern
public enum ComputeCache implements Function<ClusterSpec, ComputeServiceContext> {
  
  INSTANCE;
 
  private static final Logger LOG = LoggerFactory.getLogger(ComputeCache.class);

  @Override
  public ComputeServiceContext apply(ClusterSpec arg0) {
    return cache.getUnchecked(new Key(arg0));
  }
 
  public void invalidate(ClusterSpec arg0) {
    cache.invalidate(new Key(arg0));
  }
 
  // this should prevent recreating the same compute context twice
  @VisibleForTesting
  final LoadingCache<Key, ComputeServiceContext> cache = CacheBuilder.newBuilder().build(
        new CacheLoader<Key, ComputeServiceContext>(){
              
        @Override
        public ComputeServiceContext load(Key arg0) {
          LOG.debug("creating new ComputeServiceContext {}", arg0);
          ContextBuilder builder;

          if (arg0.overrideApiMetadata != null) {
            builder = ContextBuilder.newBuilder(arg0.overrideApiMetadata)
              .credentials(arg0.identity, arg0.credential)
              .overrides(arg0.overrides)
              .modules(arg0.modules);
          } else {
            builder = ContextBuilder.newBuilder(arg0.provider)
            .credentials(arg0.identity, arg0.credential)
            .overrides(arg0.overrides)
            .modules(arg0.modules);
          }
         
          if (arg0.endpoint != null)
            builder.endpoint(arg0.endpoint);
          ComputeServiceContext context = new IgnoreCloseComputeServiceContext(
                builder.buildView(ComputeServiceContext.class));
          LOG.debug("created new ComputeServiceContext {}", context);
          context.utils().eventBus().register(ComputeCache.this);
          return context;
        }
   
    }
  );

  @Subscribe
  @AllowConcurrentEvents
  public void onStart(StatementOnNodeSubmission event) {
    LOG.info(">> running {} on node({})", event.getStatement(), event.getNode().getId());
    if (LOG.isDebugEnabled()) {
      LOG.debug(">> script for {} on node({})\n{}", new Object[] { event.getStatement(), event.getNode().getId(),
          event.getStatement().render(OsFamily.UNIX) });
    }
  }

  @Subscribe
  @AllowConcurrentEvents
  public void onFailure(StatementOnNodeFailure event) {
    LOG.error("<< error running {} on node({}): {}", new Object[] { event.getStatement(), event.getNode().getId(),
        event.getCause().getMessage() }, event.getCause());
  }

  @Subscribe
  @AllowConcurrentEvents
  public void onSuccess(StatementOnNodeCompletion event) {
    ExecResponse arg0 = event.getResponse();
    if (arg0.getExitStatus() != 0) {
      LOG.error("<< error running {} on node({}): {}", new Object[] { event.getStatement(), event.getNode().getId(),
          arg0 });
    } else {
      LOG.info("<< success executing {} on node({}): {}", new Object[] { event.getStatement(),
          event.getNode().getId(), arg0 });
    }

  }
 
  private static class IgnoreCloseComputeServiceContext
    extends ForwardingObject implements ComputeServiceContext {

    private final ComputeServiceContext context;

    public IgnoreCloseComputeServiceContext(final ComputeServiceContext context) {
      this.context = context;
      Runtime.getRuntime().addShutdownHook(new Thread() {
         @Override
         public void run() {
           LOG.debug("closing ComputeServiceContext {}", context);
           context.close();
         }
       });
    }

    @Override
    protected ComputeServiceContext delegate() {
       return context;
    }

    @Override
    public ComputeService getComputeService() {
      return delegate().getComputeService();
    }

    @Override
    @Deprecated
    public <S, A> RestContext<S, A> getProviderSpecificContext() {
      return delegate().<S, A>getProviderSpecificContext();
    }

    @Override
    @Deprecated
    public Map<String, Credentials> getCredentialStore() {
      return delegate().getCredentialStore();
    }

    @Override
    @Deprecated
    public Map<String, Credentials> credentialStore() {
      return delegate().credentialStore();
    }

    @Override
    public Utils getUtils() {
      return delegate().getUtils();
    }

    @Override
    public Utils utils() {
      return delegate().utils();
    }

    @Override
    public void close() {
       /* Do nothing. The instance is closed by the builder */
    }

    @Override
    public TypeToken<?> getBackendType() {
      return delegate().getBackendType();
    }

    @Override
    public <C extends Context> C unwrap(TypeToken<C> type) {
      return delegate().<C>unwrap(type);
    }

    @Override
    public <C extends Context> C unwrap(Class<C> clazz) {
      return delegate().<C>unwrap(clazz);
    }

    @Override
    public <C extends Context> C unwrap() {
      return delegate().<C>unwrap();
    }

  }

  public static final Map<String, ApiMetadata> COMPUTE_APIS = Maps.uniqueIndex(Apis.viewableAs(ComputeServiceContext.class),
      Apis.idFunction());
 
  public static final Map<String, ProviderMetadata> COMPUTE_PROVIDERS = Maps.uniqueIndex(Providers.viewableAs(ComputeServiceContext.class),
      Providers.idFunction());
    
  public static final Set<String> COMPUTE_KEYS = ImmutableSet.copyOf(Iterables.concat(COMPUTE_PROVIDERS.keySet(), COMPUTE_APIS.keySet()));

  /**
   * configurable properties, scoped to a provider.
   */
  public static final Iterable<String> PROVIDER_PROPERTIES = ImmutableSet.of(
    "endpoint", "api", "apiversion", "iso3166-codes","nodes");

  /**
   * Key class for the compute context cache
   */
  private static class Key {
    private String provider;
    private String endpoint;
    private String identity;
    private String credential;
    private String clusterName;
    private ApiMetadata overrideApiMetadata;
   
    private final String key;
    private final Properties overrides;
    private final Set<Module> modules;


    public Key(ClusterSpec spec) {
      provider = spec.getProvider();
      endpoint = spec.getEndpoint();
      identity = spec.getIdentity();
      credential = spec.getCredential();
      clusterName = spec.getClusterName();

      key = Objects.toStringHelper("").omitNullValues()
            .add("provider", provider)
            .add("endpoint", endpoint)
            .add("identity", identity)
            .add("clusterName", clusterName).toString();
      Configuration jcloudsConfig = spec.getConfigurationForKeysWithPrefix("jclouds");
     
      // jclouds configuration for providers are not prefixed with jclouds.
      for (String key : COMPUTE_KEYS) {
        for (String property : PROVIDER_PROPERTIES) {
          String prefixedProperty = "jclouds." + key + "." + property;

          if (jcloudsConfig.containsKey(prefixedProperty)) {
            jcloudsConfig.setProperty(key + "." + property,
                jcloudsConfig.getProperty(prefixedProperty));
          }
        }
      }
      overrides = ConfigurationConverter.getProperties(jcloudsConfig);

      if ("aws-ec2".equals(spec.getProvider()) && spec.getTemplate().getImageId() != null) {
        enableAWSEC2LazyImageFetching(spec);
      }

      if ("stub".equals(spec.getProvider())) {
        modules = ImmutableSet.<Module>of(new SLF4JLoggingModule(), new DryRunModule());
      } else if ("byon".equals(spec.getProvider()) && !spec.getByonNodes().isEmpty()) {
        overrideApiMetadata = new BYONApiMetadata()
          .toBuilder()
          .defaultModule(BYONComputeServiceContextModule.class)
          .build();

        modules = ImmutableSet.<Module>of(new SLF4JLoggingModule(),
                                          new EnterpriseConfigurationModule(),
                                          new SshjSshClientModule(),
                                          new CacheNodeStoreModule(ImmutableMap.<String,Node>copyOf(spec.getByonNodes())));
      } else {
        modules = ImmutableSet.<Module>of(new SLF4JLoggingModule(),
            new EnterpriseConfigurationModule(), new SshjSshClientModule());
      }
    }

    /**
     * AWS EC2 specific optimisation to avoid running queries across
     * all regiosn when the image-id is know from the property file
     *
     * @param spec
     */
    private void enableAWSEC2LazyImageFetching(ClusterSpec spec) {
      overrides.setProperty(PROPERTY_EC2_AMI_QUERY, "");
      overrides.setProperty(PROPERTY_EC2_CC_AMI_QUERY, "");

      String[] parts = StringUtils.split(spec.getTemplate().getImageId(), '/');
      checkArgument(parts.length == 2, "Expected to find image-id = region/ami-id");

      overrides.setProperty(PROPERTY_REGION, parts[0]);
    }

    @Override
    public boolean equals(Object that) {
      if (that instanceof Key) {
        return Objects.equal(this.key, ((Key)that).key)
          && Objects.equal(overrides, ((Key)that).overrides);
      }
      return false;
    }

    @Override
    public int hashCode() {
      return Objects.hashCode(key, overrides);
    }
   
    @Override
    public String toString() {
      return Objects.toStringHelper(this)
        .add("provider", provider)
        .add("identity", identity)
        .add("overrides", overrides)
        .add("clusterName", clusterName)
        .toString();
    }
  }

}
TOP

Related Classes of org.apache.whirr.service.IgnoreCloseComputeServiceContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.