Package org.jclouds.nodepool.internal

Source Code of org.jclouds.nodepool.internal.EagerNodePoolComputeServiceAdapter

/*
* 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.nodepool.internal;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_GROUP;
import static org.jclouds.nodepool.config.NodePoolProperties.MAX_SIZE;
import static org.jclouds.nodepool.config.NodePoolProperties.MIN_SIZE;
import static org.jclouds.nodepool.config.NodePoolProperties.POOL_ADMIN_ACCESS;
import static org.jclouds.nodepool.config.NodePoolProperties.REMOVE_DESTROYED;

import java.util.Set;

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

import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.Logger;
import org.jclouds.nodepool.Backend;
import org.jclouds.scriptbuilder.statements.login.AdminAccess;

import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;

/**
* An eager {@link NodePoolComputeService}. Eagerly builds and maintains a pool of nodes. It's only
* "started" after min nodes are allocated and available.
*
* @author David Alves
*
*/
@Singleton
public class EagerNodePoolComputeServiceAdapter extends BaseNodePoolComputeServiceAdapter {

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

   private final int maxSize;
   private final int minSize;
   private final boolean removeDestroyed;

   @Inject
   public EagerNodePoolComputeServiceAdapter(@Backend Supplier<ComputeService> backendComputeService,
            @Backend Supplier<Template> backendTemplate, @Named(BACKEND_GROUP) String poolGroupPrefix,
            @Named(MAX_SIZE) int maxSize, @Named(MIN_SIZE) int minSize,
            @Named(REMOVE_DESTROYED) boolean removeDestroyed, NodeMetadataStore storage,
            @Named(POOL_ADMIN_ACCESS) String poolNodeAdminAccess, AdminAccess.Configuration configuration) {
      super(backendComputeService, backendTemplate, poolGroupPrefix, storage, poolNodeAdminAccess, configuration);
      this.maxSize = maxSize;
      this.minSize = minSize;
      this.removeDestroyed = removeDestroyed;
   }

   @PostConstruct
   public void startEagerPool() {
      Set<? extends NodeMetadata> backendNodes = getBackendNodes();
      int currentNodes = backendNodes.size();
      int newNodes = backendNodes.size() < minSize ? minSize - backendNodes.size() : 0;
      logger.info(
               ">> initializing nodepool [backend provider: %s]. [existing nodes: %s, min nodes: %s, allocating: %s ]",
               backendComputeService.get().getClass().getSimpleName(), currentNodes, minSize, newNodes);
      if (backendNodes.size() < minSize) {
         addToPool(minSize - backendNodes.size());
      }
      logger.info("<< pool initialized.");
   }

   @Override
   public NodeWithInitialCredentials createNodeWithGroupEncodedIntoName(String group, String name, Template template) {
      int count = 1;
      synchronized (this) {
         TemplateOptions options = template.getOptions().clone();

         // if no user is provided we set the pool's user
         if (options.getLoginUser() == null) {
            options.overrideLoginCredentials(LoginCredentials.fromCredentials(checkNotNull(initialCredentialsBuilder
                     .build().getAdminCredentials())));
         }

         logger.info(">> assigning pool node to frontend group %s", group);
         Set<NodeMetadata> backendNodes = getBackendNodes();
         checkState(!backendNodes.isEmpty());
         Set<NodeMetadata> frontendNodes = metadataStore.loadAll(backendNodes);
         checkState(frontendNodes.size() + count <= maxSize,
                  "cannot add more nodes to pool [requested: %s, current: %s, max: %s]", count, frontendNodes.size(),
                  maxSize);

         SetView<NodeMetadata> availableNodes = Sets.difference(backendNodes, frontendNodes);

         if (availableNodes.size() < 1) {
            if (backendNodes.size() < maxSize && backendNodes.size() + count <= maxSize) {
               logger.info(
                        ">> all pool nodes are assigned, requiring additional nodes [requested: %s, current: %s, next: %s, max: %s]",
                        count, frontendNodes.size(), frontendNodes.size() + 1, maxSize);
               addToPool(count);
               // update backend and available sets, no need to update frontend
               backendNodes = getBackendNodes();
               availableNodes = Sets.difference(backendNodes, frontendNodes);
               logger.info("<< additional nodes added to the pool and ready");
            } else {
               logger.error("maximum pool size reached (%s)", maxSize);
               throw new IllegalStateException(String.format("maximum pool size reached (%s)", maxSize));
            }
         }
         NodeMetadata userNode = Iterables.get(availableNodes, 0);
         NodeMetadata node = metadataStore.store(userNode, options, group);
         logger.info("pool node assigned");
         return new NodeWithInitialCredentials(node);
      }
   }

   @Override
   public synchronized void destroyNode(String id) {
      checkState(getNode(id) != null);
      logger.info(">> destroying node %s", id);
      metadataStore.deleteMapping(id);
      if (removeDestroyed) {
         backendComputeService.get().destroyNode(id);
         if (currentSize() < minSize) {
            logger.info(">> policy is remove destroyed node and pool "
                     + "would fall below minsize, replacing node with id %s", id);
            Set<? extends NodeMetadata> replacement = addToPool(1);
            logger.info("<< node %s replaced with %s", id, Iterables.getOnlyElement(replacement));
         }
      }
      // TODO we should allow the user to hook a way to "clean" the node
      else {

      }
      logger.info("<< node destroyed %s", id);
   }

   @Override
   public int currentSize() {
      return getBackendNodes().size();
   }

   @Override
   public int idleNodes() {
      Set<NodeMetadata> backendNodes = getBackendNodes();
      Set<NodeMetadata> frontendNodes = metadataStore.loadAll(backendNodes);
      return backendNodes.size() - frontendNodes.size();
   }

   @Override
   public int maxNodes() {
      return maxSize;
   }

   @Override
   public int minNodes() {
      return minSize;
   }

   @Override
   public int usedNodes() {
      return metadataStore.loadAll(getBackendNodes()).size();
   }

}
TOP

Related Classes of org.jclouds.nodepool.internal.EagerNodePoolComputeServiceAdapter

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.