blob: c2b193c6c3220635b1553e0377893d12f909fe85 [file] [log] [blame]
/*
* 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.digitalocean.compute.strategy;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.contains;
import static com.google.common.collect.Iterables.filter;
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 static org.jclouds.digitalocean.compute.util.LocationNamingUtils.extractRegionId;
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.digitalocean.DigitalOceanApi;
import org.jclouds.digitalocean.compute.options.DigitalOceanTemplateOptions;
import org.jclouds.digitalocean.domain.Droplet;
import org.jclouds.digitalocean.domain.DropletCreation;
import org.jclouds.digitalocean.domain.Image;
import org.jclouds.digitalocean.domain.Region;
import org.jclouds.digitalocean.domain.Size;
import org.jclouds.digitalocean.domain.options.CreateDropletOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.Logger;
import com.google.common.base.Predicate;
import com.google.common.primitives.Ints;
/**
* Implementation of the Compute Service for the DigitalOcean API.
*/
public class DigitalOceanComputeServiceAdapter implements ComputeServiceAdapter<Droplet, Size, Image, Region> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final DigitalOceanApi api;
private final Predicate<Integer> nodeRunningPredicate;
private final Predicate<Integer> nodeStoppedPredicate;
private final Predicate<Integer> nodeTerminatedPredicate;
@Inject
DigitalOceanComputeServiceAdapter(DigitalOceanApi api,
@Named(TIMEOUT_NODE_RUNNING) Predicate<Integer> nodeRunningPredicate,
@Named(TIMEOUT_NODE_SUSPENDED) Predicate<Integer> nodeStoppedPredicate,
@Named(TIMEOUT_NODE_TERMINATED) Predicate<Integer> nodeTerminatedPredicate) {
this.api = checkNotNull(api, "api cannot be null");
this.nodeRunningPredicate = checkNotNull(nodeRunningPredicate, "nodeRunningPredicate cannot be null");
this.nodeStoppedPredicate = checkNotNull(nodeStoppedPredicate, "nodeStoppedPredicate cannot be null");
this.nodeTerminatedPredicate = checkNotNull(nodeTerminatedPredicate, "nodeTerminatedPredicate cannot be null");
}
@Override
public NodeAndInitialCredentials<Droplet> createNodeWithGroupEncodedIntoName(String group, final String name,
Template template) {
DigitalOceanTemplateOptions templateOptions = template.getOptions().as(DigitalOceanTemplateOptions.class);
CreateDropletOptions.Builder options = CreateDropletOptions.builder();
// DigitalOcean specific options
if (!templateOptions.getSshKeyIds().isEmpty()) {
options.addSshKeyIds(templateOptions.getSshKeyIds());
}
if (templateOptions.getPrivateNetworking() != null) {
options.privateNetworking(templateOptions.getPrivateNetworking());
}
if (templateOptions.getBackupsEnabled() != null) {
options.backupsEnabled(templateOptions.getBackupsEnabled());
}
// Find the location where the Droplet has to be created
int regionId = extractRegionId(template.getLocation());
DropletCreation dropletCreation = api.getDropletApi().create(name,
Integer.parseInt(template.getImage().getProviderId()),
Integer.parseInt(template.getHardware().getProviderId()), regionId, 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(dropletCreation.getEventId());
Droplet droplet = api.getDropletApi().get(dropletCreation.getId());
LoginCredentials defaultCredentials = LoginCredentials.builder().user("root")
.privateKey(templateOptions.getLoginPrivateKey()).build();
return new NodeAndInitialCredentials<Droplet>(droplet, String.valueOf(droplet.getId()), defaultCredentials);
}
@Override
public Iterable<Image> listImages() {
return api.getImageApi().list();
}
@Override
public Iterable<Size> listHardwareProfiles() {
return api.getSizesApi().list();
}
@Override
public Iterable<Region> listLocations() {
return api.getRegionApi().list();
}
@Override
public Iterable<Droplet> listNodes() {
return api.getDropletApi().list();
}
@Override
public Iterable<Droplet> listNodesByIds(final Iterable<String> ids) {
return filter(listNodes(), new Predicate<Droplet>() {
@Override
public boolean apply(Droplet droplet) {
return contains(ids, String.valueOf(droplet.getId()));
}
});
}
@Override
public Image getImage(String 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 imageId = Ints.tryParse(id);
return imageId != null ? api.getImageApi().get(imageId) : api.getImageApi().get(id);
}
@Override
public Droplet getNode(String id) {
return api.getDropletApi().get(Integer.valueOf(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 event = api.getDropletApi().destroy(Integer.valueOf(id), true);
nodeTerminatedPredicate.apply(event);
}
@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 event = api.getDropletApi().reboot(Integer.valueOf(id));
nodeRunningPredicate.apply(event);
}
@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 event = api.getDropletApi().powerOn(Integer.valueOf(id));
nodeRunningPredicate.apply(event);
}
@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 event = api.getDropletApi().powerOff(Integer.valueOf(id));
nodeStoppedPredicate.apply(event);
}
}