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

org.jclouds.digitalocean2.compute.DigitalOcean2ComputeServiceAdapter Maven / Gradle / Ivy

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.jclouds.digitalocean2.compute;

import static shaded.com.google.common.base.Preconditions.checkState;
import static shaded.com.google.common.base.Predicates.notNull;
import static shaded.com.google.common.collect.Iterables.concat;
import static shaded.com.google.common.collect.Iterables.contains;
import static shaded.com.google.common.collect.Iterables.filter;
import static shaded.com.google.common.collect.Iterables.transform;
import static shaded.com.google.common.collect.Sets.newHashSet;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;

import java.util.List;
import java.util.Set;

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

import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.digitalocean2.DigitalOcean2Api;
import org.jclouds.digitalocean2.compute.internal.ImageInRegion;
import org.jclouds.digitalocean2.compute.options.DigitalOcean2TemplateOptions;
import org.jclouds.digitalocean2.domain.Droplet;
import org.jclouds.digitalocean2.domain.DropletCreate;
import org.jclouds.digitalocean2.domain.Image;
import org.jclouds.digitalocean2.domain.Region;
import org.jclouds.digitalocean2.domain.Size;
import org.jclouds.digitalocean2.domain.options.CreateDropletOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.Logger;

import shaded.com.google.common.base.Function;
import shaded.com.google.common.base.Predicate;
import shaded.com.google.common.primitives.Ints;

/**
 * Implementation of the Compute Service for the DigitalOcean API.
 */
public class DigitalOcean2ComputeServiceAdapter implements ComputeServiceAdapter {

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

   private final DigitalOcean2Api api;
   private final Predicate nodeRunningPredicate;
   private final Predicate nodeStoppedPredicate;
   private final Predicate nodeTerminatedPredicate;

   @Inject DigitalOcean2ComputeServiceAdapter(DigitalOcean2Api api,
         @Named(TIMEOUT_NODE_RUNNING) Predicate nodeRunningPredicate,
         @Named(TIMEOUT_NODE_SUSPENDED) Predicate nodeStoppedPredicate,
         @Named(TIMEOUT_NODE_TERMINATED) Predicate nodeTerminatedPredicate) {
      this.api = api;
      this.nodeRunningPredicate = nodeRunningPredicate;
      this.nodeStoppedPredicate = nodeStoppedPredicate;
      this.nodeTerminatedPredicate = nodeTerminatedPredicate;
   }

   private void setUserDataIfSupported(Template template, CreateDropletOptions.Builder options, String userData) {
      @SuppressWarnings("unchecked")
      List regionFeatures = (List) template.getLocation().getMetadata().get("features");
      if (regionFeatures.contains("metadata")) {
         options.userData(userData);
      } else {
         logger.debug(">> region %s does not support metadata, ignoring provided user data", template.getLocation()
               .getId());
      }
   }

   @Override
   public NodeAndInitialCredentials createNodeWithGroupEncodedIntoName(String group, final String name,
         Template template) {
      DigitalOcean2TemplateOptions templateOptions = template.getOptions().as(DigitalOcean2TemplateOptions.class);
      CreateDropletOptions.Builder options = CreateDropletOptions.builder();

      // DigitalOcean specific options
      options.privateNetworking(templateOptions.getPrivateNetworking());
      options.backupsEnabled(templateOptions.getBackupsEnabled());
      if (!templateOptions.getSshKeyIds().isEmpty()) {
         options.addSshKeyIds(templateOptions.getSshKeyIds());
      }

      // In DigitalOcean, user_data is a SINGLE string, NOT a map!
      // Encoding tags or anything else than user_data in here breaks their functionality.
      if (null != templateOptions.getUserData()) {
         setUserDataIfSupported(template, options, new String(templateOptions.getUserData()));
      } else if (null != templateOptions.getUserMetadata().get("user_data")) {
         // Backwards compatible variant, getting userData from userMetaData map.
         setUserDataIfSupported(template, options, templateOptions.getUserMetadata().get("user_data"));
      }

      DropletCreate dropletCreated = api.dropletApi().create(name,
            template.getLocation().getId(),
            template.getHardware().getProviderId(),
            template.getImage().getProviderId(),
            options.build());

      // We have to actively wait until the droplet has been provisioned until
      // we can build the entire Droplet object we want to return
      nodeRunningPredicate.apply(dropletCreated.droplet().id());
      Droplet droplet = api.dropletApi().get(dropletCreated.droplet().id());

      LoginCredentials defaultCredentials = LoginCredentials.builder().user("root")
            .privateKey(templateOptions.getLoginPrivateKey()).build();

      return new NodeAndInitialCredentials(droplet, String.valueOf(droplet.id()), defaultCredentials);
   }

   @Override
   public Iterable listImages() {
      // Images can claim to be available in a region that is currently marked as "unavailable". We shouldn't return
      // the images scoped to those regions.
      final Set availableRegionsIds = newHashSet(transform(listLocations(), new Function() {
         @Override
         public String apply(Region input) {
            return input.slug();
         }
      }));

      // Public images re globally available, but non-public ones can only be available in certain regions.
      // For these kind of images, return one instance of an ImageInRegion for each region where the image is
      // available. This way we can properly scope global and concrete images so they can be properly looked up.
      return concat(filter(api.imageApi().list().concat().transform(new Function>() {
         @Override
         public Iterable apply(final Image image) {
            return transform(image.regions(), new Function() {
               @Override
               public ImageInRegion apply(String region) {
                  return availableRegionsIds.contains(region) ? ImageInRegion.create(image, region) : null;
               }
            });
         }
      }), notNull()));
   }

   @Override
   public Iterable listHardwareProfiles() {
      return filter(api.sizeApi().list().concat(), new Predicate() {
         @Override
         public boolean apply(Size size) {
            return size.available();
         }
      });
   }

   @Override
   public Iterable listLocations() {
      // DigitalOcean lists regions that are unavailable for droplet creation
      return filter(api.regionApi().list().concat(), new Predicate() {
         @Override
         public boolean apply(Region region) {
            return region.available();
         }
      });
   }

   @Override
   public Iterable listNodes() {
      return api.dropletApi().list().concat();
   }

   @Override
   public Iterable listNodesByIds(final Iterable ids) {
      return filter(listNodes(), new Predicate() {
         @Override
         public boolean apply(Droplet droplet) {
            return contains(ids, String.valueOf(droplet.id()));
         }
      });
   }

   @Override
   public ImageInRegion getImage(String id) {
      String region = ImageInRegion.extractRegion(id);
      String imageId = ImageInRegion.extractImageId(id);
      // The id of the image can be an id or a slug. Use the corresponding method of the API depending on what is
      // provided. If it can be parsed as a number, use the method to get by ID. Otherwise, get by slug.
      Integer numericId = Ints.tryParse(imageId);
      Image image = numericId == null ? api.imageApi().get(imageId) : api.imageApi().get(numericId);
      return image == null ? null : ImageInRegion.create(image, region);
   }

   @Override
   public Droplet getNode(String id) {
      return api.dropletApi().get(Integer.parseInt(id));
   }

   @Override
   public void destroyNode(String id) {
      // We have to wait here, as the api does not properly populate the state
      // but fails if there is a pending event
      int dropletId = Integer.parseInt(id);
      api.dropletApi().delete(dropletId);
      checkState(nodeTerminatedPredicate.apply(dropletId), "node was not destroyed in the configured timeout");
   }

   @Override
   public void rebootNode(String id) {
      // We have to wait here, as the api does not properly populate the state
      // but fails if there is a pending event
      int dropletId = Integer.parseInt(id);
      api.dropletApi().reboot(dropletId);
      checkState(nodeRunningPredicate.apply(dropletId), "node did not restart in the configured timeout");
   }

   @Override
   public void resumeNode(String id) {
      // We have to wait here, as the api does not properly populate the state
      // but fails if there is a pending event
      int dropletId = Integer.parseInt(id);
      api.dropletApi().powerOn(dropletId);
      checkState(nodeRunningPredicate.apply(dropletId), "node did not started in the configured timeout");
   }

   @Override
   public void suspendNode(String id) {
      // We have to wait here, as the api does not properly populate the state
      // but fails if there is a pending event
      int dropletId = Integer.parseInt(id);
      api.dropletApi().powerOff(dropletId);
      checkState(nodeStoppedPredicate.apply(dropletId), "node did not stop in the configured timeout");
   }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy