blob: 9185edfb55fb36188082a21799b7264c412c7832 [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.googlecomputeengine.compute.extensions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.jclouds.googlecloud.internal.ListPages.concat;
import static org.jclouds.googlecomputeengine.compute.predicates.NetworkFirewallPredicates.equalsIpPermission;
import static org.jclouds.googlecomputeengine.compute.predicates.NetworkFirewallPredicates.providesIpPermission;
import static org.jclouds.googlecomputeengine.compute.strategy.CreateNodesWithGroupEncodedIntoNameThenAddToSet.DEFAULT_INTERNAL_NETWORK_RANGE;
import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.OPERATION_COMPLETE_INTERVAL;
import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.OPERATION_COMPLETE_TIMEOUT;
import static org.jclouds.googlecomputeengine.options.ListOptions.Builder.filter;
import static org.jclouds.util.Predicates2.retry;
import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.compute.domain.SecurityGroup;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.domain.Location;
import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
import org.jclouds.googlecomputeengine.compute.domain.NetworkAndAddressRange;
import org.jclouds.googlecomputeengine.compute.functions.Resources;
import org.jclouds.googlecomputeengine.domain.Firewall;
import org.jclouds.googlecomputeengine.domain.Instance;
import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface;
import org.jclouds.googlecomputeengine.domain.Network;
import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.googlecomputeengine.options.FirewallOptions;
import org.jclouds.googlecomputeengine.options.ListOptions;
import org.jclouds.net.domain.IpPermission;
import org.jclouds.net.domain.IpProtocol;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.Atomics;
/** This implementation sets {@linkplain SecurityGroup#getId()} to {@link Network#name()}. */
public final class GoogleComputeEngineSecurityGroupExtension implements SecurityGroupExtension {
private final GoogleComputeEngineApi api;
private final Resources resources;
private final GroupNamingConvention.Factory namingConvention;
private final LoadingCache<NetworkAndAddressRange, Network> networkCreator;
private final Function<Network, SecurityGroup> groupConverter;
private final Predicate<AtomicReference<Operation>> operationDone;
private final long operationCompleteCheckInterval;
private final long operationCompleteCheckTimeout;
@Inject GoogleComputeEngineSecurityGroupExtension(GoogleComputeEngineApi api, Resources resources,
GroupNamingConvention.Factory namingConvention,
LoadingCache<NetworkAndAddressRange, Network> networkCreator, Function<Network, SecurityGroup> groupConverter,
Predicate<AtomicReference<Operation>> operationDone,
@Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval,
@Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout) {
this.api = api;
this.resources = resources;
this.namingConvention = namingConvention;
this.networkCreator = networkCreator;
this.groupConverter = groupConverter;
this.operationCompleteCheckInterval = operationCompleteCheckInterval;
this.operationCompleteCheckTimeout = operationCompleteCheckTimeout;
this.operationDone = operationDone;
}
@Override
public Set<SecurityGroup> listSecurityGroups() {
return FluentIterable.from(concat(api.networks().list())).transform(groupConverter).toSet();
}
@Override
public Set<SecurityGroup> listSecurityGroupsInLocation(final Location location) {
return listSecurityGroups();
}
@Override
public Set<SecurityGroup> listSecurityGroupsForNode(String id) {
Instance instance = resources.instance(URI.create(checkNotNull(id, "id")));
if (instance == null) {
return ImmutableSet.of();
}
ImmutableSet.Builder builder = ImmutableSet.builder();
for (NetworkInterface nwInterface : instance.networkInterfaces()) {
Network nw = resources.network(nwInterface.network());
SecurityGroup grp = groupForTagsInNetwork(nw, instance.tags().items());
if (grp != null) {
builder.add(grp);
}
}
return builder.build();
}
@Override
public SecurityGroup getSecurityGroupById(String id) {
checkNotNull(id, "id");
Network network = api.networks().get(id);
if (network == null) {
return null;
}
return groupConverter.apply(network);
}
@Override
public SecurityGroup createSecurityGroup(String name, Location location) {
return createSecurityGroup(name);
}
public SecurityGroup createSecurityGroup(String name) {
checkNotNull(name, "name");
NetworkAndAddressRange nAr = NetworkAndAddressRange.create(name, DEFAULT_INTERNAL_NETWORK_RANGE, null);
Network nw = networkCreator.apply(nAr);
return groupConverter.apply(nw);
}
@Override
public boolean removeSecurityGroup(String id) {
checkNotNull(id, "id");
if (api.networks().get(id) == null) {
return false;
}
ListOptions options = filter("network eq .*/" + id);
FluentIterable<Firewall> fws = FluentIterable.from(concat(api.firewalls().list(options)));
for (Firewall fw : fws) {
AtomicReference<Operation> operation = Atomics
.newReference(api.firewalls().delete(fw.name()));
retry(operationDone, operationCompleteCheckTimeout, operationCompleteCheckInterval, MILLISECONDS)
.apply(operation);
checkState(operation.get().httpErrorStatusCode() == null,
"Could not delete firewall, operation failed" + operation);
}
AtomicReference<Operation> operation = Atomics.newReference(api.networks().delete(id));
retry(operationDone, operationCompleteCheckTimeout, operationCompleteCheckInterval, MILLISECONDS)
.apply(operation);
checkState(operation.get().httpErrorStatusCode() == null,
"Could not insert network, operation failed" + operation);
return true;
}
@Override
public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) {
checkNotNull(group, "group");
checkNotNull(ipPermission, "ipPermission");
checkNotNull(api.networks().get(group.getId()) == null, "network for group is null");
ListOptions options = filter("network eq .*/" + group.getName());
if (Iterables
.any(concat(api.firewalls().list(options)), providesIpPermission(ipPermission))) {
// Permission already exists.
return group;
}
FirewallOptions fwOptions = new FirewallOptions();
String uniqueFwName = namingConvention.createWithoutPrefix().uniqueNameForGroup(group.getName());
fwOptions.name(uniqueFwName);
fwOptions.network(group.getUri());
if (!ipPermission.getGroupIds().isEmpty()) {
fwOptions.sourceTags(ipPermission.getGroupIds());
}
if (!ipPermission.getCidrBlocks().isEmpty()) {
fwOptions.sourceRanges(ipPermission.getCidrBlocks());
}
List<String> ports = Lists.newArrayList();
if (ipPermission.getFromPort() > 0) {
if (ipPermission.getFromPort() == ipPermission.getToPort()) {
ports.add(String.valueOf(ipPermission.getToPort()));
} else {
ports.add(ipPermission.getFromPort() + "-" + ipPermission.getToPort());
}
}
fwOptions.addAllowedRule(Firewall.Rule.create(ipPermission.getIpProtocol().value().toLowerCase(), ports));
AtomicReference<Operation> operation = Atomics.newReference(
api.firewalls().createInNetwork(uniqueFwName, group.getUri(), fwOptions));
retry(operationDone, operationCompleteCheckTimeout, operationCompleteCheckInterval, MILLISECONDS)
.apply(operation);
checkState(operation.get().httpErrorStatusCode() == null,
"Could not insert firewall, operation failed" + operation);
return getSecurityGroupById(group.getId());
}
@Override
public SecurityGroup addIpPermission(IpProtocol protocol, int fromPort, int toPort,
Multimap<String, String> tenantIdGroupNamePairs, Iterable<String> cidrBlocks, Iterable<String> groupIds,
SecurityGroup group) {
IpPermission.Builder permBuilder = IpPermission.builder();
permBuilder.ipProtocol(protocol);
permBuilder.fromPort(fromPort);
permBuilder.toPort(toPort);
permBuilder.groupIds(groupIds);
permBuilder.cidrBlocks(cidrBlocks);
return addIpPermission(permBuilder.build(), group);
}
@Override
public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) {
checkNotNull(group, "group");
checkNotNull(ipPermission, "ipPermission");
checkNotNull(api.networks().get(group.getId()) == null, "network for group is null");
ListOptions options = filter("network eq .*/" + group.getName());
FluentIterable<Firewall> fws = FluentIterable.from(concat(api.firewalls().list(options)));
for (Firewall fw : fws) {
if (equalsIpPermission(ipPermission).apply(fw)) {
AtomicReference<Operation> operation = Atomics
.newReference(api.firewalls().delete(fw.name()));
retry(operationDone, operationCompleteCheckTimeout, operationCompleteCheckInterval, MILLISECONDS)
.apply(operation);
checkState(operation.get().httpErrorStatusCode() == null,
"Could not delete firewall, operation failed" + operation);
}
}
return getSecurityGroupById(group.getId());
}
@Override
public SecurityGroup removeIpPermission(IpProtocol protocol, int fromPort, int toPort,
Multimap<String, String> tenantIdGroupNamePairs, Iterable<String> cidrBlocks, Iterable<String> groupIds,
SecurityGroup group) {
IpPermission.Builder permBuilder = IpPermission.builder();
permBuilder.ipProtocol(protocol);
permBuilder.fromPort(fromPort);
permBuilder.toPort(toPort);
permBuilder.groupIds(groupIds);
permBuilder.cidrBlocks(cidrBlocks);
return removeIpPermission(permBuilder.build(), group);
}
@Override
public boolean supportsTenantIdGroupNamePairs() {
return false;
}
@Override
public boolean supportsTenantIdGroupIdPairs() {
return false;
}
@Override
public boolean supportsGroupIds() {
return true;
}
@Override
public boolean supportsPortRangesForGroups() {
return true;
}
@Override
public boolean supportsExclusionCidrBlocks() {
return false;
}
private SecurityGroup groupForTagsInNetwork(Network nw, final Collection<String> tags) {
ListOptions opts = filter("network eq .*/" + nw.name());
List<Firewall> fws = FluentIterable.from(concat(api.firewalls().list(opts)))
.filter(new Predicate<Firewall>() {
@Override public boolean apply(final Firewall input) {
// If any of the targetTags on the firewall apply or the firewall has no target tags...
return Iterables.any(input.targetTags(), Predicates.in(tags)) || Predicates.equalTo(0)
.apply(input.targetTags().size());
}
}).toList();
if (fws.isEmpty()) {
return null;
}
return groupConverter.apply(nw);
}
}