diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java index 32d4d599f9..fe25a683cd 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java @@ -43,6 +43,7 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.internal.PersistNodeCredentials; @@ -113,12 +114,13 @@ public class CloudStackComputeService extends BaseComputeService { Function, Multimap> orphanedGroupsByZoneId, GroupNamingConvention.Factory namingConvention, Supplier> zoneIdToZone, - Optional imageExtension) { + Optional imageExtension, + Optional securityGroupExtension) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory, - persistNodeCredentials, timeouts, userExecutor, imageExtension); + persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension); this.zoneIdToZone = checkNotNull(zoneIdToZone, "zoneIdToZone"); this.client = checkNotNull(client, "client"); this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap"); diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java index 92e0b6c4da..fa4c2147be 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java @@ -32,6 +32,8 @@ import javax.inject.Singleton; import org.jclouds.cloudstack.CloudStackClient; import org.jclouds.cloudstack.compute.CloudStackComputeService; import org.jclouds.cloudstack.compute.extensions.CloudStackImageExtension; +import org.jclouds.cloudstack.compute.functions.CloudStackSecurityGroupToSecurityGroup; +import org.jclouds.cloudstack.compute.functions.IngressRuleToIpPermission; import org.jclouds.cloudstack.compute.functions.OrphanedGroupsByZoneId; import org.jclouds.cloudstack.compute.functions.ServiceOfferingToHardware; import org.jclouds.cloudstack.compute.functions.TemplateToImage; @@ -46,6 +48,7 @@ import org.jclouds.cloudstack.compute.strategy.BasicNetworkOptionsConverter; import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter; import org.jclouds.cloudstack.compute.strategy.OptionsConverter; import org.jclouds.cloudstack.domain.FirewallRule; +import org.jclouds.cloudstack.domain.IngressRule; import org.jclouds.cloudstack.domain.IPForwardingRule; import org.jclouds.cloudstack.domain.Network; import org.jclouds.cloudstack.domain.NetworkType; @@ -77,6 +80,7 @@ import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.extensions.ImageExtension; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Location; +import org.jclouds.net.domain.IpPermission; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; @@ -112,6 +116,10 @@ public class CloudStackComputeServiceContextModule extends }).to(CloudStackComputeServiceAdapter.class); bind(new TypeLiteral>() { }).to(VirtualMachineToNodeMetadata.class); + bind(new TypeLiteral>() { + }).to(CloudStackSecurityGroupToSecurityGroup.class); + bind(new TypeLiteral>() { + }).to(IngressRuleToIpPermission.class); bind(new TypeLiteral>() { }).to(TemplateToImage.class); bind(new TypeLiteral>() { diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroup.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroup.java new file mode 100644 index 0000000000..10a3e314e4 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroup.java @@ -0,0 +1,74 @@ +/* + * 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.cloudstack.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; + +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.cloudstack.domain.IngressRule; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Iterables; +import com.google.inject.Inject; + + +/** + * A function for transforming a CloudStack-specific SecurityGroup into a generic + * SecurityGroup object. + * + * @author Andrew Bayer + */ +@Singleton +public class CloudStackSecurityGroupToSecurityGroup implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + protected final Function ruleToPermission; + + @Inject + public CloudStackSecurityGroupToSecurityGroup(Function ruleToPermission) { + this.ruleToPermission = ruleToPermission; + } + + @Override + public SecurityGroup apply(org.jclouds.cloudstack.domain.SecurityGroup group) { + SecurityGroupBuilder builder = new SecurityGroupBuilder(); + + builder.id(group.getId()); + builder.providerId(group.getId()); + builder.name(group.getName()); + builder.ownerId(group.getAccount()); + builder.ipPermissions(transform(group.getIngressRules(), ruleToPermission)); + + return builder.build(); + } +} \ No newline at end of file diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermission.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermission.java new file mode 100644 index 0000000000..ed0456836b --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermission.java @@ -0,0 +1,55 @@ +/* + * 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.cloudstack.compute.functions; + +import javax.annotation.Resource; +import javax.inject.Named; + +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.cloudstack.domain.IngressRule; +import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; + +import com.google.common.base.Function; + + +/** + * A function for transforming a CloudStack-specific IngressRule into a generic + * IpPermission object. + * + * @author Andrew Bayer + */ +public class IngressRuleToIpPermission implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + public IngressRuleToIpPermission() { + } + + @Override + public IpPermission apply(IngressRule rule) { + IpPermission.Builder builder = IpPermission.builder(); + builder.ipProtocol(IpProtocol.fromValue(rule.getProtocol())); + builder.fromPort(rule.getStartPort()); + builder.toPort(rule.getEndPort()); + builder.cidrBlock(rule.getCIDR()); + + return builder.build(); + } +} \ No newline at end of file diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroupTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroupTest.java new file mode 100644 index 0000000000..22420defec --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/CloudStackSecurityGroupToSecurityGroupTest.java @@ -0,0 +1,80 @@ +/* + * 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.cloudstack.compute.functions; + +import static com.google.common.collect.Iterables.transform; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.jclouds.cloudstack.domain.IngressRule; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableSet; + +/** + * @author Andrew Bayer + */ +@Test(groups = "unit", testName = "CloudStackSecurityGroupToSecurityGroupTest") +public class CloudStackSecurityGroupToSecurityGroupTest { + + private static final IngressRuleToIpPermission ruleConverter = new IngressRuleToIpPermission(); + + @Test + public void testApply() { + IngressRule ruleToConvert = IngressRule.builder() + .id("some-id") + .account("some-account") + .securityGroupName("some-group-name") + .protocol(IpProtocol.TCP.toString()) + .startPort(10) + .endPort(20) + .CIDR("0.0.0.0/0") + .build(); + + org.jclouds.cloudstack.domain.SecurityGroup origGroup = org.jclouds.cloudstack.domain.SecurityGroup.builder() + .id("some-id") + .name("some-group") + .description("some-description") + .account("some-account") + .ingressRules(ImmutableSet.of(ruleToConvert)) + .build(); + + CloudStackSecurityGroupToSecurityGroup parser = createGroupParser(); + + SecurityGroup group = parser.apply(origGroup); + + assertEquals(group.getId(), origGroup.getId()); + assertEquals(group.getProviderId(), origGroup.getId()); + assertEquals(group.getName(), origGroup.getName()); + assertEquals(group.getOwnerId(), origGroup.getAccount()); + assertEquals(group.getIpPermissions(), ImmutableSet.copyOf(transform(origGroup.getIngressRules(), ruleConverter))); + } + + private CloudStackSecurityGroupToSecurityGroup createGroupParser() { + CloudStackSecurityGroupToSecurityGroup parser = new CloudStackSecurityGroupToSecurityGroup(ruleConverter); + + return parser; + } + +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermissionTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermissionTest.java new file mode 100644 index 0000000000..0f5babb7b8 --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/IngressRuleToIpPermissionTest.java @@ -0,0 +1,61 @@ +/* + * 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.cloudstack.compute.functions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jclouds.cloudstack.domain.IngressRule; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + + +/** + * Tests for the function for transforming a cloudstack specific IngressRule into a generic + * IpPermission object. + * + * @author Andrew Bayer + */ +public class IngressRuleToIpPermissionTest { + + @Test + public void testApplyWithTCP() { + IngressRule ruleToConvert = IngressRule.builder() + .id("some-id") + .account("some-account") + .securityGroupName("some-group-name") + .protocol(IpProtocol.TCP.toString()) + .startPort(10) + .endPort(20) + .CIDR("0.0.0.0/0") + .build(); + + IngressRuleToIpPermission converter = new IngressRuleToIpPermission(); + + IpPermission convertedPerm = converter.apply(ruleToConvert); + + assertEquals(convertedPerm.getIpProtocol(), IpProtocol.fromValue(ruleToConvert.getProtocol())); + assertEquals(convertedPerm.getFromPort(), ruleToConvert.getStartPort()); + assertEquals(convertedPerm.getToPort(), ruleToConvert.getEndPort()); + assertEquals(convertedPerm.getCidrBlocks(), ImmutableSet.of("0.0.0.0/0")); + assertTrue(convertedPerm.getTenantIdGroupNamePairs().size() == 0); + assertTrue(convertedPerm.getGroupIds().size() == 0); + } +} diff --git a/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj b/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj index bc000a22c0..a6bc49cabe 100644 --- a/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj +++ b/apis/ec2/src/main/clojure/org/jclouds/ec2/security_group2.clj @@ -21,9 +21,9 @@ org.jclouds.ec2.security-group2 (:require (org.jclouds [compute2 :as compute]) [org.jclouds.ec2.ebs2 :as ebs]) - (:import org.jclouds.ec2.domain.IpProtocol - org.jclouds.ec2.domain.SecurityGroup - org.jclouds.ec2.services.SecurityGroupClient)) + (:import org.jclouds.ec2.domain.SecurityGroup + org.jclouds.ec2.services.SecurityGroupClient + org.jclouds.net.domain.IpProtocol)) (defn #^SecurityGroupClient sg-service diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java index f90e938cae..403f59225b 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionToIndexedFormParams.java @@ -19,9 +19,9 @@ package org.jclouds.ec2.binders; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import org.jclouds.ec2.domain.IpPermission; import org.jclouds.ec2.util.IpPermissions; import org.jclouds.http.HttpRequest; +import org.jclouds.net.domain.IpPermission; import org.jclouds.rest.Binder; /** diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java index d9260f9089..4bead83a8f 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindIpPermissionsToIndexedFormParams.java @@ -19,9 +19,9 @@ package org.jclouds.ec2.binders; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import org.jclouds.ec2.domain.IpPermission; import org.jclouds.ec2.util.IpPermissions; import org.jclouds.http.HttpRequest; +import org.jclouds.net.domain.IpPermission; import org.jclouds.rest.Binder; import com.google.common.collect.ImmutableMultimap; diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java index 9e6949cc38..c62a259d70 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java @@ -55,6 +55,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.functions.GroupNamingConvention.Factory; import org.jclouds.compute.internal.BaseComputeService; @@ -128,12 +129,13 @@ public class EC2ComputeService extends BaseComputeService { ConcurrentMap credentialsMap, @Named("SECURITY") LoadingCache securityGroupMap, Optional imageExtension, GroupNamingConvention.Factory namingConvention, - @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames) { + @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames, + Optional securityGroupExtension) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory, - persistNodeCredentials, timeouts, userExecutor, imageExtension); + persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension); this.client = client; this.credentialsMap = credentialsMap; this.securityGroupMap = securityGroupMap; diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java index 5e209c939d..2a1b495f03 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java @@ -31,6 +31,7 @@ import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.domain.Image; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.loaders.RegionAndIdToImage; @@ -137,5 +138,9 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod return Optional.of(i.getInstance(ImageExtension.class)); } + @Override + protected Optional provideSecurityGroupExtension(Injector i) { + return Optional.of(i.getInstance(SecurityGroupExtension.class)); + } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java index 06b524fd1a..c93baeef16 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java @@ -29,18 +29,22 @@ import org.jclouds.compute.ComputeService; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata.Status; +import org.jclouds.compute.domain.SecurityGroup; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.extensions.EC2ImageExtension; +import org.jclouds.ec2.compute.extensions.EC2SecurityGroupExtension; import org.jclouds.ec2.compute.functions.AddElasticIpsToNodemetadata; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.functions.CredentialsForInstance; import org.jclouds.ec2.compute.functions.EC2ImageParser; +import org.jclouds.ec2.compute.functions.EC2SecurityGroupToSecurityGroup; import org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstance; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData; @@ -129,8 +133,12 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { }).to(PasswordCredentialsFromWindowsInstance.class); bind(new TypeLiteral>() { }).to(EC2ImageParser.class); + bind(new TypeLiteral>() { + }).to(EC2SecurityGroupToSecurityGroup.class); bind(new TypeLiteral() { }).to(EC2ImageExtension.class); + bind(new TypeLiteral() { + }).to(EC2SecurityGroupExtension.class); } /** diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtension.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtension.java new file mode 100644 index 0000000000..4b09d56bf3 --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtension.java @@ -0,0 +1,384 @@ +/* + * 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.ec2.compute.extensions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static com.google.common.base.Predicates.notNull; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Iterables.toArray; +import static com.google.common.collect.Iterables.transform; + +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; + +import org.jclouds.Constants; +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.functions.GroupNamingConvention.Factory; +import org.jclouds.domain.Location; +import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; +import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.domain.UserIdGroupPair; +import org.jclouds.location.Region; +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.Supplier; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.UncheckedTimeoutException; + +/** + * An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation + * is optional by providers. + * + * @author Andrew Bayer + */ +public class EC2SecurityGroupExtension implements SecurityGroupExtension { + + protected final EC2Client client; + protected final ListeningExecutorService userExecutor; + protected final Supplier> regions; + protected final Function groupConverter; + protected final Supplier> locations; + protected final LoadingCache groupCreator; + protected final Factory namingConvention; + + @Inject + public EC2SecurityGroupExtension(EC2Client client, + @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, + @Region Supplier> regions, + Function groupConverter, + @Memoized Supplier> locations, + @Named("SECURITY") LoadingCache groupCreator, + GroupNamingConvention.Factory namingConvention) { + + this.client = checkNotNull(client, "client"); + this.userExecutor = checkNotNull(userExecutor, "userExecutor"); + this.regions = checkNotNull(regions, "regions"); + this.groupConverter = checkNotNull(groupConverter, "groupConverter"); + this.locations = checkNotNull(locations, "locations"); + this.groupCreator = checkNotNull(groupCreator, "groupCreator"); + this.namingConvention = checkNotNull(namingConvention, "namingConvention"); + } + + @Override + public Set listSecurityGroups() { + Iterable rawGroups = pollSecurityGroups(); + Iterable groups = transform(filter(rawGroups, notNull()), + groupConverter); + return ImmutableSet.copyOf(groups); + } + + + @Override + public Set listSecurityGroupsInLocation(final Location location) { + String region = AWSUtils.getRegionFromLocationOrNull(location); + if (region == null) { + return ImmutableSet.of(); + } + return listSecurityGroupsInLocation(region); + } + + public Set listSecurityGroupsInLocation(String region) { + Iterable rawGroups = pollSecurityGroupsByRegion(region); + Iterable groups = transform(filter(rawGroups, notNull()), + groupConverter); + return ImmutableSet.copyOf(groups); + } + + @Override + public Set listSecurityGroupsForNode(String id) { + checkNotNull(id, "id"); + String[] parts = AWSUtils.parseHandle(id); + String region = parts[0]; + String instanceId = parts[1]; + + RunningInstance instance = getOnlyElement(Iterables.concat(client.getInstanceServices().describeInstancesInRegion(region, instanceId))); + + if (instance == null) { + return ImmutableSet.of(); + } + + Set groupNames = instance.getGroupNames(); + Set rawGroups = + client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, Iterables.toArray(groupNames, String.class)); + + return ImmutableSet.copyOf(transform(filter(rawGroups, notNull()), groupConverter)); + } + + @Override + public SecurityGroup getSecurityGroupById(String id) { + checkNotNull(id, "id"); + String[] parts = AWSUtils.parseHandle(id); + String region = parts[0]; + String groupId = parts[1]; + + Set rawGroups = + client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupId); + + return getOnlyElement(transform(filter(rawGroups, notNull()), groupConverter)); + } + + @Override + public SecurityGroup createSecurityGroup(String name, Location location) { + String region = AWSUtils.getRegionFromLocationOrNull(location); + if (region != null) { + return createSecurityGroup(name, region); + } else { + return null; + } + } + + public SecurityGroup createSecurityGroup(String name, String region) { + String markerGroup = namingConvention.create().sharedNameForGroup(name); + RegionNameAndIngressRules regionAndName = new RegionNameAndIngressRules(region, markerGroup, new int[] {}, + false); + + groupCreator.getUnchecked(regionAndName); + + return getSecurityGroupById(regionAndName.slashEncode()); + } + + @Override + public boolean removeSecurityGroup(String id) { + checkNotNull(id, "id"); + String[] parts = AWSUtils.parseHandle(id); + String region = parts[0]; + String groupName = parts[1]; + + if (client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupName).size() > 0) { + client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName); + // TODO: test this clear happens + groupCreator.invalidate(new RegionNameAndIngressRules(region, groupName, null, false)); + return true; + } + + return false; + } + + + @Override + public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) { + String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation()); + String name = group.getName(); + + if (ipPermission.getCidrBlocks().size() > 0) { + for (String cidr : ipPermission.getCidrBlocks()) { + client.getSecurityGroupServices(). + authorizeSecurityGroupIngressInRegion(region, + name, + ipPermission.getIpProtocol(), + ipPermission.getFromPort(), + ipPermission.getToPort(), + cidr); + } + } + + if (ipPermission.getTenantIdGroupNamePairs().size() > 0) { + for (String userId : ipPermission.getTenantIdGroupNamePairs().keySet()) { + for (String groupName : ipPermission.getTenantIdGroupNamePairs().get(userId)) { + client.getSecurityGroupServices(). + authorizeSecurityGroupIngressInRegion(region, + name, + new UserIdGroupPair(userId, groupName)); + } + } + } + System.out.println("group: " + group); + return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode()); + } + + @Override + public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort, + Multimap tenantIdGroupNamePairs, + Iterable ipRanges, + Iterable groupIds, SecurityGroup group) { + String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation()); + String name = group.getName(); + + if (Iterables.size(ipRanges) > 0) { + for (String cidr : ipRanges) { + client.getSecurityGroupServices(). + authorizeSecurityGroupIngressInRegion(region, + name, + protocol, + startPort, + endPort, + cidr); + } + } + + if (tenantIdGroupNamePairs.size() > 0) { + for (String userId : tenantIdGroupNamePairs.keySet()) { + for (String groupName : tenantIdGroupNamePairs.get(userId)) { + client.getSecurityGroupServices(). + authorizeSecurityGroupIngressInRegion(region, + name, + new UserIdGroupPair(userId, groupName)); + } + } + } + + return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode()); + } + + @Override + public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) { + String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation()); + String name = group.getName(); + + if (ipPermission.getCidrBlocks().size() > 0) { + for (String cidr : ipPermission.getCidrBlocks()) { + client.getSecurityGroupServices(). + revokeSecurityGroupIngressInRegion(region, + name, + ipPermission.getIpProtocol(), + ipPermission.getFromPort(), + ipPermission.getToPort(), + cidr); + } + } + + if (ipPermission.getTenantIdGroupNamePairs().size() > 0) { + for (String userId : ipPermission.getTenantIdGroupNamePairs().keySet()) { + for (String groupName : ipPermission.getTenantIdGroupNamePairs().get(userId)) { + client.getSecurityGroupServices(). + revokeSecurityGroupIngressInRegion(region, + name, + new UserIdGroupPair(userId, groupName)); + } + } + } + + return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode()); + } + + @Override + public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort, + Multimap tenantIdGroupNamePairs, + Iterable ipRanges, + Iterable groupIds, SecurityGroup group) { + String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation()); + String name = group.getName(); + + if (Iterables.size(ipRanges) > 0) { + for (String cidr : ipRanges) { + client.getSecurityGroupServices(). + revokeSecurityGroupIngressInRegion(region, + name, + protocol, + startPort, + endPort, + cidr); + } + } + + if (tenantIdGroupNamePairs.size() > 0) { + for (String userId : tenantIdGroupNamePairs.keySet()) { + for (String groupName : tenantIdGroupNamePairs.get(userId)) { + client.getSecurityGroupServices(). + revokeSecurityGroupIngressInRegion(region, + name, + new UserIdGroupPair(userId, groupName)); + } + } + } + + return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode()); + } + + @Override + public boolean supportsTenantIdGroupNamePairs() { + return true; + } + + @Override + public boolean supportsGroupIds() { + return false; + } + + @Override + public boolean supportsPortRangesForGroups() { + return false; + } + + protected Iterable pollSecurityGroups() { + Iterable> groups + = transform(regions.get(), allSecurityGroupsInRegion()); + + return concat(groups); + } + + + protected Iterable pollSecurityGroupsByRegion(String region) { + return allSecurityGroupsInRegion().apply(region); + } + + protected Function> allSecurityGroupsInRegion() { + return new Function>() { + + @Override + public Set apply(String from) { + return client.getSecurityGroupServices().describeSecurityGroupsInRegion(from); + } + + }; + } + + protected Location findLocationWithId(final String locationId) { + if (locationId == null) + return null; + try { + Location location = Iterables.find(locations.get(), new Predicate() { + + @Override + public boolean apply(Location input) { + return input.getId().equals(locationId); + } + + }); + return location; + + } catch (NoSuchElementException e) { + return null; + } + } + +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroup.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroup.java new file mode 100644 index 0000000000..7e0ff667cd --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroup.java @@ -0,0 +1,95 @@ +/* + * 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.ec2.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.domain.Location; +import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpPermission; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Iterables; +import com.google.inject.Inject; + + +/** + * A function for transforming an EC2-specific SecurityGroup into a generic + * SecurityGroup object. + * + * @author Andrew Bayer + */ +@Singleton +public class EC2SecurityGroupToSecurityGroup implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + protected final Supplier> locations; + + @Inject + public EC2SecurityGroupToSecurityGroup(@Memoized Supplier> locations) { + this.locations = checkNotNull(locations, "locations"); + } + + @Override + public SecurityGroup apply(org.jclouds.ec2.domain.SecurityGroup group) { + SecurityGroupBuilder builder = new SecurityGroupBuilder(); + Location location = findLocationWithId(group.getRegion()); + builder.location(location); + builder.id(group.getName()); + builder.providerId(group.getId()); + builder.name(group.getName()); + builder.ipPermissions(group); + builder.ownerId(group.getOwnerId()); + + return builder.build(); + } + + private Location findLocationWithId(final String locationId) { + if (locationId == null) + return null; + try { + Location location = Iterables.find(locations.get(), new Predicate() { + + @Override + public boolean apply(Location input) { + return input.getId().equals(locationId); + } + + }); + return location; + + } catch (NoSuchElementException e) { + logger.debug("couldn't match instance location %s in: %s", locationId, locations.get()); + return null; + } + } +} \ No newline at end of file diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java index 0d541645db..de3b9062a0 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java @@ -27,10 +27,10 @@ import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.UserIdGroupPair; import org.jclouds.ec2.services.SecurityGroupClient; import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpProtocol; import com.google.common.base.Predicate; import com.google.common.cache.CacheLoader; diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java deleted file mode 100644 index 91c75e7912..0000000000 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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.ec2.domain; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Set; - -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - -/** - * - * @see - * @author Adrian Cole - */ -public class IpPermission { - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private int fromPort; - private int toPort; - private IpProtocol ipProtocol; - private Multimap userIdGroupPairs = LinkedHashMultimap.create(); - private Set groupIds = Sets.newLinkedHashSet(); - private Set ipRanges = Sets.newLinkedHashSet(); - - public Builder fromPort(int fromPort) { - this.fromPort = fromPort; - return this; - } - - public Builder toPort(int toPort) { - this.toPort = toPort; - return this; - } - - public Builder ipProtocol(IpProtocol ipProtocol) { - this.ipProtocol = checkNotNull(ipProtocol, "ipProtocol"); - return this; - } - - public Builder userIdGroupPair(String userId, String groupNameOrId) { - this.userIdGroupPairs.put(checkNotNull(userId, "userId"), checkNotNull(groupNameOrId, "groupNameOrId of %s", userId)); - return this; - } - - public Builder userIdGroupPairs(Multimap userIdGroupPairs) { - this.userIdGroupPairs.putAll(checkNotNull(userIdGroupPairs, "userIdGroupPairs")); - return this; - } - - public Builder ipRange(String ipRange) { - this.ipRanges.add(ipRange); - return this; - } - - public Builder ipRanges(Iterable ipRanges) { - Iterables.addAll(this.ipRanges, checkNotNull(ipRanges, "ipRanges")); - return this; - } - - public Builder groupId(String groupId) { - this.groupIds.add(checkNotNull(groupId, "groupId")); - return this; - } - - public Builder groupIds(Iterable groupIds) { - Iterables.addAll(this.groupIds, checkNotNull(groupIds, "groupIds")); - return this; - } - - public IpPermission build() { - return new IpPermission(ipProtocol, fromPort, toPort, userIdGroupPairs, groupIds, ipRanges); - } - } - - private final int fromPort; - private final int toPort; - private final Multimap userIdGroupPairs; - private final Set groupIds; - private final IpProtocol ipProtocol; - private final Set ipRanges; - - public IpPermission(IpProtocol ipProtocol, int fromPort, int toPort, Multimap userIdGroupPairs, - Iterable groupIds, Iterable ipRanges) { - this.fromPort = fromPort; - this.toPort = toPort; - this.userIdGroupPairs = ImmutableMultimap.copyOf(checkNotNull(userIdGroupPairs, "userIdGroupPairs")); - this.ipProtocol = checkNotNull(ipProtocol, "ipProtocol"); - this.groupIds = ImmutableSet.copyOf(checkNotNull(groupIds, "groupIds")); - this.ipRanges = ImmutableSet.copyOf(checkNotNull(ipRanges, "ipRanges")); - } - - /** - * Start of port range for the TCP and UDP protocols, or an ICMP type number. - * An ICMP type number of -1 indicates a wildcard (i.e., any ICMP type - * number). - */ - public int getFromPort() { - return fromPort; - } - - /** - * End of port range for the TCP and UDP protocols, or an ICMP code. An ICMP - * code of -1 indicates a wildcard (i.e., any ICMP code). - */ - public int getToPort() { - return toPort; - } - - /** - * List of security group and user ID pairs. - */ - public Multimap getUserIdGroupPairs() { - return userIdGroupPairs; - } - - /** - * List of security group Ids - */ - public Set getGroupIds() { - return groupIds; - } - - /** - * IP protocol - */ - public IpProtocol getIpProtocol() { - return ipProtocol; - } - - /** - * IP ranges. - */ - public Set getIpRanges() { - return ipRanges; - } - - @Override - public int hashCode() { - return Objects.hashCode(fromPort, toPort, groupIds, ipProtocol, ipRanges, userIdGroupPairs); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null || getClass() != obj.getClass()) - return false; - IpPermission that = IpPermission.class.cast(obj); - return Objects.equal(this.fromPort, that.fromPort) && Objects.equal(this.toPort, that.toPort) - && Objects.equal(this.groupIds, that.groupIds) && Objects.equal(this.ipProtocol, that.ipProtocol) - && Objects.equal(this.ipRanges, that.ipRanges) - && Objects.equal(this.userIdGroupPairs, that.userIdGroupPairs); - } - - @Override - public String toString() { - return Objects.toStringHelper(this).omitNullValues().add("fromPort", fromPort == -1 ? null : fromPort) - .add("toPort", toPort == -1 ? null : toPort).add("groupIds", groupIds.size() == 0 ? null : groupIds) - .add("ipProtocol", ipProtocol).add("ipRanges", ipRanges.size() == 0 ? null : ipRanges) - .add("userIdGroupPairs", userIdGroupPairs.size() == 0 ? null : userIdGroupPairs).toString(); - } - -} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java index ca5cdbb1d7..caccebcb43 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/SecurityGroup.java @@ -21,6 +21,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Set; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.net.domain.IpPermission; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java index 4d074eedf8..0de10f9d31 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java @@ -30,12 +30,12 @@ import org.jclouds.Fallbacks.VoidOnNotFoundOr404; import org.jclouds.aws.filters.FormSigner; import org.jclouds.ec2.binders.BindGroupNamesToIndexedFormParams; import org.jclouds.ec2.binders.BindUserIdGroupPairToSourceSecurityGroupFormParams; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.domain.UserIdGroupPair; import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.EndpointParam; import org.jclouds.rest.annotations.Fallback; diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java index 3874bfb1d4..23c8272a16 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupClient.java @@ -17,10 +17,10 @@ package org.jclouds.ec2.services; import java.util.Set; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.domain.UserIdGroupPair; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.net.domain.IpProtocol; /** * Provides access to EC2 via their REST API. diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java b/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java index ac32cda3da..9c938f0f42 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/util/IpPermissions.java @@ -21,8 +21,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; import java.util.Map.Entry; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.util.Maps2; import com.google.common.annotations.Beta; @@ -58,7 +58,7 @@ public class IpPermissions extends IpPermission { headers.put("IpPermissions.%d.ToPort", permission.getToPort() + ""); String prefix = "IpPermissions.%d.IpRanges."; int i = 0; - for (String cidrIp : checkNotNull(permission.getIpRanges(), "cidrIps")) { + for (String cidrIp : checkNotNull(permission.getCidrBlocks(), "cidrIps")) { headers.put(prefix + i++ + ".CidrIp", cidrIp); } prefix = "IpPermissions.%d.Groups."; @@ -68,15 +68,15 @@ public class IpPermissions extends IpPermission { } prefix = "IpPermissions.%d.Groups."; i = 0; - for (Entry userIdGroupNamePair : checkNotNull(permission.getUserIdGroupPairs(), - "userIdGroupNamePairs").entries()) { - headers.put(prefix + i + ".UserId", userIdGroupNamePair.getKey()); - headers.put(prefix + i + ".GroupName", userIdGroupNamePair.getValue()); + for (Entry tenantIdGroupNamePair : checkNotNull(permission.getTenantIdGroupNamePairs(), + "tenantIdGroupNamePairs").entries()) { + headers.put(prefix + i + ".UserId", tenantIdGroupNamePair.getKey()); + headers.put(prefix + i + ".GroupName", tenantIdGroupNamePair.getValue()); i++; } prefix = "IpPermissions.%d.IpRanges."; i = 0; - for (String cidrIp : checkNotNull(permission.getIpRanges(), "cidrIps")) { + for (String cidrIp : checkNotNull(permission.getCidrBlocks(), "cidrIps")) { headers.put(prefix + i++ + ".CidrIp", cidrIp); } return Multimaps.forMap(Maps2.transformKeys(headers, new Function() { @@ -165,7 +165,7 @@ public class IpPermissions extends IpPermission { } public IpPermissions toVPCSecurityGroups(Iterable groupIds) { - return new IpPermissions(getIpProtocol(), getFromPort(), getToPort(), getUserIdGroupPairs(), groupIds, + return new IpPermissions(getIpProtocol(), getFromPort(), getToPort(), getTenantIdGroupNamePairs(), groupIds, ImmutableSet. of()); } } @@ -189,8 +189,8 @@ public class IpPermissions extends IpPermission { checkNotNull(groupName, "groupName"))); } - public IpPermissions toEC2SecurityGroups(Multimap userIdGroupNamePairs) { - return new IpPermissions(getIpProtocol(), getFromPort(), getToPort(), userIdGroupNamePairs, getGroupIds(), + public IpPermissions toEC2SecurityGroups(Multimap tenantIdGroupNamePairs) { + return new IpPermissions(getIpProtocol(), getFromPort(), getToPort(), tenantIdGroupNamePairs, getGroupIds(), ImmutableSet. of()); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java index 5b4dc6d920..980ef35b84 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/IpPermissionHandler.java @@ -19,9 +19,9 @@ package org.jclouds.ec2.xml; import static org.jclouds.util.SaxUtils.currentOrNull; import static org.jclouds.util.SaxUtils.equalsOrSuffix; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.http.functions.ParseSax; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import org.xml.sax.SAXException; /** @@ -66,14 +66,14 @@ public class IpPermissionHandler extends ParseSax.HandlerForGeneratedRequestWith // EC2) builder.toPort(Integer.parseInt(currentOrNegative(currentText))); } else if (equalsOrSuffix(qName, "cidrIp")) { - builder.ipRange(currentOrNull(currentText)); + builder.cidrBlock(currentOrNull(currentText)); } else if (equalsOrSuffix(qName, "userId")) { this.userId = currentOrNull(currentText); } else if (equalsOrSuffix(qName, "groupName") || equalsOrSuffix(qName, "groupId")) { this.groupId = currentOrNull(currentText); } else if (equalsOrSuffix(qName, "item")) { if (userId != null && groupId != null) - builder.userIdGroupPair(userId, groupId); + builder.tenantIdGroupNamePair(userId, groupId); userId = groupId = null; } currentText = new StringBuilder(); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java index 584e6d92e2..1fc8de931e 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java @@ -40,7 +40,6 @@ import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.Image.EbsBlockDevice; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.InstanceType; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.PublicIpInstanceIdPair; import org.jclouds.ec2.domain.Reservation; @@ -49,6 +48,7 @@ import org.jclouds.ec2.domain.Volume.InstanceInitiatedShutdownBehavior; import org.jclouds.ec2.predicates.InstanceHasIpAddress; import org.jclouds.ec2.predicates.InstanceStateRunning; import org.jclouds.http.HttpResponseException; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.predicates.SocketOpen; import org.jclouds.scriptbuilder.ScriptBuilder; import org.jclouds.scriptbuilder.domain.OsFamily; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java index 67af652dd6..3b8bd758f2 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java @@ -42,7 +42,6 @@ import org.jclouds.ec2.domain.Image.Architecture; import org.jclouds.ec2.domain.Image.ImageType; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.InstanceType; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RootDeviceType; @@ -58,6 +57,7 @@ import org.jclouds.ec2.predicates.VolumeAttached; import org.jclouds.ec2.predicates.VolumeAvailable; import org.jclouds.http.HttpResponseException; import org.jclouds.io.Payloads; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.predicates.SocketOpen; import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.OsFamily; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java index 67db38b44a..6334c9a9b0 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java @@ -39,7 +39,6 @@ import org.jclouds.ec2.EC2ApiMetadata; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.BlockDevice; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.PublicIpInstanceIdPair; import org.jclouds.ec2.domain.RunningInstance; @@ -51,6 +50,7 @@ import org.jclouds.ec2.services.ElasticBlockStoreClient; import org.jclouds.ec2.services.InstanceClient; import org.jclouds.ec2.services.KeyPairClient; import org.jclouds.ec2.services.SecurityGroupClient; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.scriptbuilder.domain.Statements; import org.jclouds.sshj.config.SshjSshClientModule; import org.jclouds.util.InetAddresses2; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionExpectTest.java new file mode 100644 index 0000000000..89e54f2893 --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionExpectTest.java @@ -0,0 +1,585 @@ +/* + * 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.ec2.compute.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.internal.BaseEC2ComputeServiceExpectTest; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Futures; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "EC2SecurityGroupExtensionExpectTest") +public class EC2SecurityGroupExtensionExpectTest extends BaseEC2ComputeServiceExpectTest { + + public void testListSecurityGroups() { + HttpRequest describeSecurityGroupsAllRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups").build()); + + HttpResponse describeSecurityGroupsAllResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_new.xml", MediaType.APPLICATION_XML)).build(); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsAllRequest, describeSecurityGroupsAllResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(describeInstanceRequest, describeInstanceResponse); + + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + Set groups = extension.listSecurityGroups(); + assertEquals(2, groups.size()); + } + + public void testListSecurityGroupsInLocation() { + HttpRequest describeSecurityGroupsAllRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups").build()); + + HttpResponse describeSecurityGroupsAllResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_new.xml", MediaType.APPLICATION_XML)).build(); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsAllRequest, describeSecurityGroupsAllResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(describeInstanceRequest, describeInstanceResponse); + + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + Set groups = extension.listSecurityGroupsInLocation(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + assertEquals(2, groups.size()); + } + + + public void testListSecurityGroupsForNode() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "sg-3c6ef654").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build(); + + HttpResponse describeInstanceWithSGResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_instances_running_securitygroups.xml", MediaType.APPLICATION_XML)).build(); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(describeInstanceRequest, describeInstanceWithSGResponse); + + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + Set groups = extension.listSecurityGroupsForNode(new RegionAndName(region, "i-2baa5550").slashEncode()); + assertEquals(1, groups.size()); + } + + public void testGetSecurityGroupById() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build(); + + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); + + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroup group = extension.getSecurityGroupById(new RegionAndName(region, "jclouds#some-group").slashEncode()); + assertEquals("sg-3c6ef654", group.getProviderId()); + assertEquals("jclouds#some-group", group.getId()); + } + + public void testCreateSecurityGroup() { + HttpRequest createSecurityGroupExtRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "CreateSecurityGroup") + .addFormParam("GroupDescription", "jclouds#some-group") + .addFormParam("GroupName", "jclouds#some-group").build()); + + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build(); + + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupExtRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); + + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroup group = extension.createSecurityGroup("some-group", new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + + assertEquals("sg-3c6ef654", group.getProviderId()); + assertEquals("jclouds#some-group", group.getId()); + } + + public void testRemoveSecurityGroup() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build(); + + HttpRequest deleteSecurityGroupRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DeleteSecurityGroup") + .addFormParam("GroupName", "jclouds#some-group").build()); + + HttpResponse deleteSecurityGroupResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/delete_securitygroup.xml", MediaType.APPLICATION_XML)).build(); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(deleteSecurityGroupRequest, deleteSecurityGroupResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); + + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + assertTrue(extension.removeSecurityGroup(new RegionAndName(region, "jclouds#some-group").slashEncode())); + } + + public void testAddIpPermissionCidrFromIpPermission() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build(); + + + HttpRequest authorizeSecurityGroupIngressRequestRange = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "AuthorizeSecurityGroupIngress") + .addFormParam("CidrIp", "0.0.0.0/0") + .addFormParam("FromPort", "22") + .addFormParam("ToPort", "40") + .addFormParam("GroupName", "jclouds#some-group") + .addFormParam("IpProtocol", "tcp").build()); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse); + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(IpProtocol.TCP); + builder.fromPort(22); + builder.toPort(40); + builder.cidrBlock("0.0.0.0/0"); + + IpPermission perm = builder.build(); + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); + groupBuilder.id("jclouds#some-group"); + groupBuilder.providerId("sg-3c6ef654"); + groupBuilder.name("jclouds#some-group"); + groupBuilder.location(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + + SecurityGroup origGroup = groupBuilder.build(); + + SecurityGroup newGroup = extension.addIpPermission(perm, origGroup); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + + assertNotNull(newPerm); + assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); + assertEquals(22, newPerm.getFromPort()); + assertEquals(40, newPerm.getToPort()); + assertEquals(1, newPerm.getCidrBlocks().size()); + assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0")); + } + + public void testAddIpPermissionCidrFromParams() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build(); + + + HttpRequest authorizeSecurityGroupIngressRequestRange = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "AuthorizeSecurityGroupIngress") + .addFormParam("CidrIp", "0.0.0.0/0") + .addFormParam("FromPort", "22") + .addFormParam("ToPort", "40") + .addFormParam("GroupName", "jclouds#some-group") + .addFormParam("IpProtocol", "tcp").build()); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse); + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); + groupBuilder.id("jclouds#some-group"); + groupBuilder.providerId("sg-3c6ef654"); + groupBuilder.name("jclouds#some-group"); + groupBuilder.location(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + + SecurityGroup origGroup = groupBuilder.build(); + + SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP, + 22, + 40, + emptyMultimap(), + ImmutableSet.of("0.0.0.0/0"), + emptyStringSet(), + origGroup); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + + assertNotNull(newPerm); + assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); + assertEquals(22, newPerm.getFromPort()); + assertEquals(40, newPerm.getToPort()); + assertEquals(1, newPerm.getCidrBlocks().size()); + assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0")); + } + + + public void testAddIpPermissionGroupFromIpPermission() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build(); + + + HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "AuthorizeSecurityGroupIngress") + .addFormParam("SourceSecurityGroupName", "jclouds#some-group") + .addFormParam("SourceSecurityGroupOwnerId", "993194456877") + .addFormParam("GroupName", "jclouds#some-group").build()); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse); + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(IpProtocol.TCP); + builder.fromPort(22); + builder.toPort(40); + builder.tenantIdGroupNamePair("993194456877", "jclouds#some-group"); + + IpPermission perm = builder.build(); + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); + groupBuilder.id("jclouds#some-group"); + groupBuilder.providerId("sg-3c6ef654"); + groupBuilder.name("jclouds#some-group"); + groupBuilder.location(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + groupBuilder.ownerId("993194456877"); + + SecurityGroup origGroup = groupBuilder.build(); + + SecurityGroup newGroup = extension.addIpPermission(perm, origGroup); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + + assertNotNull(newPerm); + assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); + assertEquals(22, newPerm.getFromPort()); + assertEquals(40, newPerm.getToPort()); + assertEquals(0, newPerm.getCidrBlocks().size()); + assertEquals(1, newPerm.getTenantIdGroupNamePairs().size()); + assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId())); + assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getName())); + } + + public void testAddIpPermissionGroupFromParams() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build(); + + + HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "AuthorizeSecurityGroupIngress") + .addFormParam("SourceSecurityGroupName", "jclouds#some-group") + .addFormParam("SourceSecurityGroupOwnerId", "993194456877") + .addFormParam("GroupName", "jclouds#some-group").build()); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse); + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); + groupBuilder.id("jclouds#some-group"); + groupBuilder.providerId("sg-3c6ef654"); + groupBuilder.name("jclouds#some-group"); + groupBuilder.ownerId("993194456877"); + groupBuilder.location(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + + SecurityGroup origGroup = groupBuilder.build(); + + ImmutableMultimap.Builder permBuilder = ImmutableMultimap.builder(); + permBuilder.put(origGroup.getOwnerId(), origGroup.getName()); + + SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP, + 22, + 40, + permBuilder.build(), + emptyStringSet(), + emptyStringSet(), + origGroup); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + + assertNotNull(newPerm); + assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); + assertEquals(22, newPerm.getFromPort()); + assertEquals(40, newPerm.getToPort()); + assertEquals(0, newPerm.getCidrBlocks().size()); + assertEquals(1, newPerm.getTenantIdGroupNamePairs().size()); + assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId())); + assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getName())); + } + + private Multimap emptyMultimap() { + return LinkedHashMultimap.create(); + } + + private Set emptyStringSet() { + return Sets.newLinkedHashSet(); + } +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpProtocol.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionLiveTest.java similarity index 55% rename from apis/ec2/src/main/java/org/jclouds/ec2/domain/IpProtocol.java rename to apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionLiveTest.java index 41e771ce58..128e1a3ff9 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpProtocol.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2SecurityGroupExtensionLiveTest.java @@ -14,35 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.ec2.domain; +package org.jclouds.ec2.compute.extensions; -import static com.google.common.base.Preconditions.checkNotNull; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.extensions.internal.BaseSecurityGroupExtensionLiveTest; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.annotations.Test; + +import com.google.inject.Module; /** - * @author Adrian Cole + * Live test for ec2 {@link SecurityGroupExtension} implementation + * + * @author Andrew Bayer * */ -public enum IpProtocol { +@Test(groups = "live", singleThreaded = true, testName = "EC2SecurityGroupExtensionLiveTest") +public class EC2SecurityGroupExtensionLiveTest extends BaseSecurityGroupExtensionLiveTest { - TCP, UDP, ICMP, ALL, UNRECOGNIZED; - - public String value() { - return this == ALL ? "-1" : name().toLowerCase(); - } - - @Override - public String toString() { - return value(); - } - - public static IpProtocol fromValue(String protocol) { - try { - if (protocol.equalsIgnoreCase("-1")) - return ALL; - return valueOf(checkNotNull(protocol, "protocol").toUpperCase()); - } catch (IllegalArgumentException e) { - return UNRECOGNIZED; - } + public EC2SecurityGroupExtensionLiveTest() { + provider = "ec2"; } } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroupTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroupTest.java new file mode 100644 index 0000000000..9c7d3b19e7 --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2SecurityGroupToSecurityGroupTest.java @@ -0,0 +1,85 @@ +/* + * 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.ec2.compute.functions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.ec2.util.IpPermissions; +import org.jclouds.net.domain.IpPermission; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableSet; + +/** + * @author Andrew Bayer + */ +@Test(groups = "unit", testName = "EC2SecurityGroupToSecurityGroupTest") +public class EC2SecurityGroupToSecurityGroupTest { + + static Location provider = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1") + .description("us-east-1").build(); + + @Test + public void testApply() { + IpPermissions authorization = IpPermissions.permitAnyProtocol(); + + org.jclouds.ec2.domain.SecurityGroup origGroup = org.jclouds.ec2.domain.SecurityGroup.builder() + .region("us-east-1") + .id("some-id") + .name("some-group") + .ownerId("some-owner") + .description("some-description") + .ipPermission(authorization) + .build(); + + EC2SecurityGroupToSecurityGroup parser = createGroupParser(ImmutableSet.of(provider)); + + SecurityGroup group = parser.apply(origGroup); + + assertEquals(group.getLocation(), provider); + assertEquals(group.getId(), origGroup.getName()); + assertEquals(group.getProviderId(), origGroup.getId()); + assertEquals(group.getName(), origGroup.getName()); + assertEquals(group.getIpPermissions(), (Set)origGroup); + assertEquals(group.getOwnerId(), origGroup.getOwnerId()); + } + + private EC2SecurityGroupToSecurityGroup createGroupParser(final ImmutableSet locations) { + Supplier> locationSupplier = new Supplier>() { + + @Override + public Set get() { + return locations; + } + + }; + + EC2SecurityGroupToSecurityGroup parser = new EC2SecurityGroupToSecurityGroup(locationSupplier); + + return parser; + } + +} diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java index 85358b340f..9597e1a313 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java @@ -29,10 +29,10 @@ import java.util.concurrent.ExecutionException; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.domain.UserIdGroupPair; import org.jclouds.ec2.services.SecurityGroupClient; +import org.jclouds.net.domain.IpProtocol; import org.testng.annotations.Test; import com.google.common.base.Predicate; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java index 24360d71a1..c22a279563 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java @@ -22,12 +22,12 @@ import java.io.IOException; import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404; import org.jclouds.Fallbacks.VoidOnNotFoundOr404; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.UserIdGroupPair; import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler; import org.jclouds.http.HttpRequest; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ReleasePayloadAndReturn; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.testng.annotations.Test; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java index 0f25935858..c6d02d2fad 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java @@ -30,10 +30,10 @@ import java.util.Set; import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; import org.jclouds.ec2.EC2ApiMetadata; import org.jclouds.ec2.EC2Client; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.domain.UserIdGroupPair; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -174,7 +174,7 @@ public class SecurityGroupClientLiveTest extends BaseComputeServiceContextLiveTe assertEventually(new GroupHasPermission(client, group2Name, new Predicate() { @Override public boolean apply(IpPermission arg0) { - return arg0.getUserIdGroupPairs().equals(ImmutableMultimap.of(group.getOwnerId(), group1Name)); + return arg0.getTenantIdGroupNamePairs().equals(ImmutableMultimap.of(group.getOwnerId(), group1Name)); } })); @@ -191,7 +191,7 @@ public class SecurityGroupClientLiveTest extends BaseComputeServiceContextLiveTe @Override public boolean apply(IpPermission arg0) { return arg0.getIpProtocol() == IpProtocol.TCP && arg0.getFromPort() == 80 && arg0.getToPort() == 80 - && arg0.getIpRanges().equals(ImmutableSet.of("0.0.0.0/0")); + && arg0.getCidrBlocks().equals(ImmutableSet.of("0.0.0.0/0")); } } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java index 6d466b7be4..03a2549342 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/util/IpPermissionsTest.java @@ -18,7 +18,7 @@ package org.jclouds.ec2.util; import static org.testng.Assert.assertEquals; -import org.jclouds.ec2.domain.IpProtocol; +import org.jclouds.net.domain.IpProtocol; import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java index 8032aaa9d7..caabd1a401 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java @@ -21,10 +21,10 @@ import static org.testng.Assert.assertEquals; import java.io.InputStream; import java.util.Set; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.http.functions.ParseSax; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import org.testng.annotations.Test; import com.google.common.collect.ImmutableMultimap; @@ -46,10 +46,10 @@ public class DescribeSecurityGroupsResponseHandlerTest extends BaseEC2HandlerTes InputStream is = getClass().getResourceAsStream("/describe_securitygroups.xml"); Set expected = ImmutableSet.of( - new SecurityGroup(defaultRegion, null, "WebServers", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Web Servers", + new SecurityGroup(defaultRegion, "sg-3c6ef654", "WebServers", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Web Servers", ImmutableSet.of(new IpPermission(IpProtocol.TCP, 80, 80, ImmutableMultimap. of(), ImmutableSet. of(), ImmutableSet.of("0.0.0.0/0")))), - new SecurityGroup(defaultRegion, null, "RangedPortsBySource", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Group A", + new SecurityGroup(defaultRegion, "sg-867309ab", "RangedPortsBySource", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Group A", ImmutableSet.of(new IpPermission(IpProtocol.TCP, 6000, 7000, ImmutableMultimap . of(), ImmutableSet. of(), ImmutableSet. of())))); @@ -69,7 +69,7 @@ public class DescribeSecurityGroupsResponseHandlerTest extends BaseEC2HandlerTes userIdGroupPairs.put("UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "jclouds#cluster#world"); Set expected = ImmutableSet.of( - new SecurityGroup(defaultRegion, null, "jclouds#cluster#world", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Cluster", + new SecurityGroup(defaultRegion, "sg-3c6ef654", "jclouds#cluster#world", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Cluster", ImmutableSet.of( new IpPermission(IpProtocol.TCP, 22, 22, ImmutableMultimap. of(), ImmutableSet. of(), ImmutableSet.of("0.0.0.0/0")), diff --git a/apis/ec2/src/test/resources/delete_securitygroup.xml b/apis/ec2/src/test/resources/delete_securitygroup.xml new file mode 100644 index 0000000000..9545778724 --- /dev/null +++ b/apis/ec2/src/test/resources/delete_securitygroup.xml @@ -0,0 +1,4 @@ + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + diff --git a/apis/ec2/src/test/resources/describe_instances_running_securitygroups.xml b/apis/ec2/src/test/resources/describe_instances_running_securitygroups.xml new file mode 100644 index 0000000000..4c07ed2e9c --- /dev/null +++ b/apis/ec2/src/test/resources/describe_instances_running_securitygroups.xml @@ -0,0 +1,74 @@ + + + f6d3252e-35e5-4ef5-b2c5-62da95dd829b + + + r-205ad944 + 993194456877 + + + sg-3c6ef654 + jclouds#some-group + + + + + i-2baa5550 + ami-aecd60c7 + + 16 + running + + ip-10-28-89-195.ec2.internal + ec2-50-16-1-166.compute-1.amazonaws.com + + jclouds#mygroup2#81 + 0 + + t1.micro + 2012-08-02T04:28:30.000Z + + us-east-1e + + default + + aki-88aa75e1 + + disabled + + 10.28.89.195 + 50.16.1.166 + + + sg-3c6ef654 + jclouds#some-group + + + x86_64 + ebs + /dev/sda1 + + + /dev/sda1 + + vol-f2d7c993 + attached + 2012-08-02T04:28:56.000Z + true + + + + paravirtual + + + + Name + mygroup2-2baa5550 + + + xen + + + + + \ No newline at end of file diff --git a/apis/ec2/src/test/resources/describe_securitygroups.xml b/apis/ec2/src/test/resources/describe_securitygroups.xml index 3a6c9b86d3..b703afa71c 100644 --- a/apis/ec2/src/test/resources/describe_securitygroups.xml +++ b/apis/ec2/src/test/resources/describe_securitygroups.xml @@ -3,6 +3,7 @@ UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM + sg-3c6ef654 WebServers Web Servers @@ -21,7 +22,8 @@ UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM - RangedPortsBySource + sg-867309ab + RangedPortsBySource Group A diff --git a/apis/ec2/src/test/resources/describe_securitygroups_empty.xml b/apis/ec2/src/test/resources/describe_securitygroups_empty.xml index 0079ee6abd..ca89f4e969 100644 --- a/apis/ec2/src/test/resources/describe_securitygroups_empty.xml +++ b/apis/ec2/src/test/resources/describe_securitygroups_empty.xml @@ -23,6 +23,7 @@ + sg-3c6ef654 jclouds#cluster#world UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM @@ -31,6 +32,7 @@ jclouds#cluster#world + sg-3c6ef654 Cluster UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM diff --git a/apis/ec2/src/test/resources/describe_securitygroups_extension_cidr.xml b/apis/ec2/src/test/resources/describe_securitygroups_extension_cidr.xml new file mode 100644 index 0000000000..f6a6f9e975 --- /dev/null +++ b/apis/ec2/src/test/resources/describe_securitygroups_extension_cidr.xml @@ -0,0 +1,24 @@ + + + + 993194456877 + sg-3c6ef654 + jclouds#some-group + jclouds#some-group + + + tcp + 22 + 40 + + + + 0.0.0.0/0 + + + + + + + \ No newline at end of file diff --git a/apis/ec2/src/test/resources/describe_securitygroups_extension_group.xml b/apis/ec2/src/test/resources/describe_securitygroups_extension_group.xml new file mode 100644 index 0000000000..126d45c4cf --- /dev/null +++ b/apis/ec2/src/test/resources/describe_securitygroups_extension_group.xml @@ -0,0 +1,26 @@ + + + + 993194456877 + jclouds#some-group + jclouds#some-group + sg-3c6ef654 + + + tcp + 22 + 40 + + + 993194456877 + sg-3c6ef654 + jclouds#some-group + + + + + + + + \ No newline at end of file diff --git a/apis/ec2/src/test/resources/describe_securitygroups_extension_new.xml b/apis/ec2/src/test/resources/describe_securitygroups_extension_new.xml new file mode 100644 index 0000000000..7d2c2111e3 --- /dev/null +++ b/apis/ec2/src/test/resources/describe_securitygroups_extension_new.xml @@ -0,0 +1,27 @@ + + + + 993194456877 + jclouds#some-group + jclouds#some-group + sg-3c6ef654 + + + + 993194456877 + sg-3a2b3c4d + group-a + Group A + + + tcp + 6000 + 7000 + + + + + + + \ No newline at end of file diff --git a/apis/ec2/src/test/resources/describe_securitygroups_extension_single.xml b/apis/ec2/src/test/resources/describe_securitygroups_extension_single.xml new file mode 100644 index 0000000000..f66e4c6c75 --- /dev/null +++ b/apis/ec2/src/test/resources/describe_securitygroups_extension_single.xml @@ -0,0 +1,12 @@ + + + + 993194456877 + jclouds#some-group + jclouds#some-group + sg-3c6ef654 + + + + \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java index 28c26e10e5..7a1094272d 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java @@ -40,6 +40,7 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.internal.PersistNodeCredentials; @@ -107,12 +108,13 @@ public class NovaComputeService extends BaseComputeService { LoadingCache securityGroupMap, LoadingCache keyPairCache, Function, Multimap> orphanedGroupsByZoneId, - GroupNamingConvention.Factory namingConvention, Optional imageExtension) { + GroupNamingConvention.Factory namingConvention, Optional imageExtension, + Optional securityGroupExtension) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory, - persistNodeCredentials, timeouts, userExecutor, imageExtension); + persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension); this.novaApi = checkNotNull(novaApi, "novaApi"); this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap"); this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java index 92a19b3493..57a341dd8b 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java @@ -38,12 +38,14 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.domain.SecurityGroup; import org.jclouds.compute.extensions.ImageExtension; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet; import org.jclouds.domain.Location; import org.jclouds.domain.LoginCredentials; import org.jclouds.functions.IdentityFunction; +import org.jclouds.net.domain.IpPermission; import org.jclouds.openstack.nova.v2_0.compute.NovaComputeService; import org.jclouds.openstack.nova.v2_0.compute.NovaComputeServiceAdapter; import org.jclouds.openstack.nova.v2_0.compute.extensions.NovaImageExtension; @@ -51,7 +53,9 @@ import org.jclouds.openstack.nova.v2_0.compute.functions.CreateSecurityGroupIfNe import org.jclouds.openstack.nova.v2_0.compute.functions.FlavorInZoneToHardware; import org.jclouds.openstack.nova.v2_0.compute.functions.ImageInZoneToImage; import org.jclouds.openstack.nova.v2_0.compute.functions.ImageToOperatingSystem; +import org.jclouds.openstack.nova.v2_0.compute.functions.NovaSecurityGroupToSecurityGroup; import org.jclouds.openstack.nova.v2_0.compute.functions.OrphanedGroupsByZoneId; +import org.jclouds.openstack.nova.v2_0.compute.functions.SecurityGroupRuleToIpPermission; import org.jclouds.openstack.nova.v2_0.compute.functions.ServerInZoneToNodeMetadata; import org.jclouds.openstack.nova.v2_0.compute.loaders.CreateUniqueKeyPair; import org.jclouds.openstack.nova.v2_0.compute.loaders.FindSecurityGroupOrCreate; @@ -60,6 +64,7 @@ import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions; import org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet; import org.jclouds.openstack.nova.v2_0.domain.FloatingIP; import org.jclouds.openstack.nova.v2_0.domain.KeyPair; +import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; import org.jclouds.openstack.nova.v2_0.domain.Server; import org.jclouds.openstack.nova.v2_0.domain.zonescoped.FlavorInZone; import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ImageInZone; @@ -108,6 +113,12 @@ public class NovaComputeServiceContextModule extends bind(new TypeLiteral>() { }).to(ServerInZoneToNodeMetadata.class); + bind(new TypeLiteral>() { + }).to(SecurityGroupRuleToIpPermission.class); + + bind(new TypeLiteral>() { + }).to(NovaSecurityGroupToSecurityGroup.class); + bind(new TypeLiteral, Multimap>>() { }).to(OrphanedGroupsByZoneId.class); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java index 04f992eae7..6b5d519080 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java @@ -28,9 +28,9 @@ import javax.inject.Singleton; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.NovaApi; import org.jclouds.openstack.nova.v2_0.domain.Ingress; -import org.jclouds.openstack.nova.v2_0.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup; import org.jclouds.openstack.nova.v2_0.domain.zonescoped.SecurityGroupInZone; import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneSecurityGroupNameAndPorts; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroup.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroup.java new file mode 100644 index 0000000000..3e8b8a699a --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroup.java @@ -0,0 +1,74 @@ +/* + * 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.openstack.nova.v2_0.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; + +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Iterables; +import com.google.inject.Inject; + + +/** + * A function for transforming a Nova-specific SecurityGroup into a generic + * SecurityGroup object. + * + * @author Andrew Bayer + */ +@Singleton +public class NovaSecurityGroupToSecurityGroup implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + protected Function ruleToPermission; + + @Inject + public NovaSecurityGroupToSecurityGroup(Function ruleToPermission) { + this.ruleToPermission = ruleToPermission; + } + + @Override + public SecurityGroup apply(org.jclouds.openstack.nova.v2_0.domain.SecurityGroup group) { + SecurityGroupBuilder builder = new SecurityGroupBuilder(); + + builder.id(group.getId()); + builder.providerId(group.getId()); + builder.ownerId(group.getTenantId()); + builder.name(group.getName()); + builder.ipPermissions(transform(group.getRules(), ruleToPermission)); + + return builder.build(); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermission.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermission.java new file mode 100644 index 0000000000..c2e5e64cd1 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermission.java @@ -0,0 +1,57 @@ +/* + * 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.openstack.nova.v2_0.compute.functions; + +import javax.annotation.Resource; +import javax.inject.Named; + +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; + +import com.google.common.base.Function; + + +/** + * A function for transforming a nova-specific SecurityGroupRule into a generic + * IpPermission object. + * + * @author Andrew Bayer + */ +public class SecurityGroupRuleToIpPermission implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + public SecurityGroupRuleToIpPermission() { + } + + @Override + public IpPermission apply(SecurityGroupRule rule) { + IpPermission.Builder builder = IpPermission.builder(); + builder.ipProtocol(rule.getIpProtocol()); + builder.fromPort(rule.getFromPort()); + builder.toPort(rule.getToPort()); + if (rule.getGroup() != null) + builder.tenantIdGroupNamePair(rule.getGroup().getTenantId(), rule.getGroup().getName()); + if (rule.getIpRange() != null) + builder.cidrBlock(rule.getIpRange()); + + return builder.build(); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java index d687e8303f..3a9322df3b 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java @@ -21,6 +21,7 @@ import java.beans.ConstructorProperties; import javax.inject.Named; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.net.domain.IpProtocol; import com.google.common.annotations.Beta; import com.google.common.base.Objects; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java index 3da080dbe6..92d58f7bb3 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java @@ -23,6 +23,7 @@ import java.beans.ConstructorProperties; import javax.inject.Named; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.net.domain.IpProtocol; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroupTest.java new file mode 100644 index 0000000000..8db1e1510f --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/NovaSecurityGroupToSecurityGroupTest.java @@ -0,0 +1,112 @@ +/* + * 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.openstack.nova.v2_0.compute.functions; + +import static com.google.common.collect.Iterables.transform; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; +import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableSet; + +/** + * @author Andrew Bayer + */ +@Test(groups = "unit", testName = "NovaSecurityGroupToSecurityGroupTest") +public class NovaSecurityGroupToSecurityGroupTest { + + private static final SecurityGroupRuleToIpPermission ruleConverter = new SecurityGroupRuleToIpPermission(); + + @Test + public void testApplyWithGroup() { + TenantIdAndName group = TenantIdAndName.builder().tenantId("tenant").name("name").build(); + + SecurityGroupRule ruleToConvert = SecurityGroupRule.builder() + .id("some-id") + .ipProtocol(IpProtocol.TCP) + .fromPort(10) + .toPort(20) + .group(group) + .parentGroupId("some-other-id") + .build(); + + org.jclouds.openstack.nova.v2_0.domain.SecurityGroup origGroup = org.jclouds.openstack.nova.v2_0.domain.SecurityGroup.builder() + .tenantId("tenant") + .id("some-id") + .name("some-group") + .description("some-description") + .rules(ruleToConvert) + .build(); + + NovaSecurityGroupToSecurityGroup parser = createGroupParser(); + + SecurityGroup newGroup = parser.apply(origGroup); + + assertEquals(newGroup.getId(), origGroup.getId()); + assertEquals(newGroup.getProviderId(), origGroup.getId()); + assertEquals(newGroup.getName(), origGroup.getName()); + assertEquals(newGroup.getOwnerId(), origGroup.getTenantId()); + assertEquals(newGroup.getIpPermissions(), ImmutableSet.copyOf(transform(origGroup.getRules(), ruleConverter))); + } + + @Test + public void testApplyWithCidr() { + SecurityGroupRule ruleToConvert = SecurityGroupRule.builder() + .id("some-id") + .ipProtocol(IpProtocol.TCP) + .fromPort(10) + .toPort(20) + .ipRange("0.0.0.0/0") + .parentGroupId("some-other-id") + .build(); + + org.jclouds.openstack.nova.v2_0.domain.SecurityGroup origGroup = org.jclouds.openstack.nova.v2_0.domain.SecurityGroup.builder() + .tenantId("tenant") + .id("some-id") + .name("some-group") + .description("some-description") + .rules(ruleToConvert) + .build(); + + NovaSecurityGroupToSecurityGroup parser = createGroupParser(); + + SecurityGroup group = parser.apply(origGroup); + + assertEquals(group.getId(), origGroup.getId()); + assertEquals(group.getProviderId(), origGroup.getId()); + assertEquals(group.getName(), origGroup.getName()); + assertEquals(group.getOwnerId(), origGroup.getTenantId()); + assertEquals(group.getIpPermissions(), ImmutableSet.copyOf(transform(origGroup.getRules(), ruleConverter))); + } + + private NovaSecurityGroupToSecurityGroup createGroupParser() { + NovaSecurityGroupToSecurityGroup parser = new NovaSecurityGroupToSecurityGroup(ruleConverter); + + return parser; + } + +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermissionTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermissionTest.java new file mode 100644 index 0000000000..1702a13764 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/SecurityGroupRuleToIpPermissionTest.java @@ -0,0 +1,86 @@ +/* + * 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.openstack.nova.v2_0.compute.functions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; +import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + + +/** + * Tests for the function for transforming a nova specific SecurityGroupRule into a generic + * IpPermission object. + * + * @author Andrew Bayer + */ +public class SecurityGroupRuleToIpPermissionTest { + + @Test + public void testApplyWithGroup() { + + TenantIdAndName group = TenantIdAndName.builder().tenantId("tenant").name("name").build(); + + SecurityGroupRule ruleToConvert = SecurityGroupRule.builder() + .id("some-id") + .ipProtocol(IpProtocol.TCP) + .fromPort(10) + .toPort(20) + .group(group) + .parentGroupId("some-other-id") + .build(); + + SecurityGroupRuleToIpPermission converter = new SecurityGroupRuleToIpPermission(); + + IpPermission convertedPerm = converter.apply(ruleToConvert); + + assertEquals(convertedPerm.getIpProtocol(), ruleToConvert.getIpProtocol()); + assertEquals(convertedPerm.getFromPort(), ruleToConvert.getFromPort()); + assertEquals(convertedPerm.getToPort(), ruleToConvert.getToPort()); + assertTrue(convertedPerm.getTenantIdGroupNamePairs().containsKey(group.getTenantId())); + assertTrue(convertedPerm.getTenantIdGroupNamePairs().containsValue(group.getName())); + assertTrue(convertedPerm.getCidrBlocks().size() == 0); + } + + @Test + public void testApplyWithCidr() { + SecurityGroupRule ruleToConvert = SecurityGroupRule.builder() + .id("some-id") + .ipProtocol(IpProtocol.TCP) + .fromPort(10) + .toPort(20) + .ipRange("0.0.0.0/0") + .parentGroupId("some-other-id") + .build(); + + SecurityGroupRuleToIpPermission converter = new SecurityGroupRuleToIpPermission(); + + IpPermission convertedPerm = converter.apply(ruleToConvert); + + assertEquals(convertedPerm.getIpProtocol(), ruleToConvert.getIpProtocol()); + assertEquals(convertedPerm.getFromPort(), ruleToConvert.getFromPort()); + assertEquals(convertedPerm.getToPort(), ruleToConvert.getToPort()); + assertEquals(convertedPerm.getCidrBlocks(), ImmutableSet.of("0.0.0.0/0")); + assertTrue(convertedPerm.getTenantIdGroupNamePairs().size() == 0); + } +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java index 1a9f4fdf44..1fa8416ae7 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiExpectTest.java @@ -24,9 +24,9 @@ import java.net.URI; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.NovaApi; import org.jclouds.openstack.nova.v2_0.domain.Ingress; -import org.jclouds.openstack.nova.v2_0.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java index 665bb4ec23..c0fb2df181 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupApiLiveTest.java @@ -20,8 +20,8 @@ import static org.testng.Assert.assertNotNull; import java.util.Set; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.domain.Ingress; -import org.jclouds.openstack.nova.v2_0.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiLiveTest; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java index 5da793f9b6..9b056b9d22 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java @@ -23,8 +23,8 @@ import javax.ws.rs.core.MediaType; import org.jclouds.json.BaseItemParserTest; import org.jclouds.json.config.GsonModule; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.config.NovaParserModule; -import org.jclouds.openstack.nova.v2_0.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java index 9f477ee21c..4e46ede804 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupListTest.java @@ -23,8 +23,8 @@ import javax.ws.rs.core.MediaType; import org.jclouds.json.BaseSetParserTest; import org.jclouds.json.config.GsonModule; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.config.NovaParserModule; -import org.jclouds.openstack.nova.v2_0.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; import org.jclouds.rest.annotations.SelectJson; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java index a5c83f4eef..784a0bdb40 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java @@ -23,8 +23,8 @@ import javax.ws.rs.core.MediaType; import org.jclouds.json.BaseItemParserTest; import org.jclouds.json.config.GsonModule; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.config.NovaParserModule; -import org.jclouds.openstack.nova.v2_0.domain.IpProtocol; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup; import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule; import org.jclouds.openstack.nova.v2_0.domain.TenantIdAndName; diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java index 1431d54769..b37e85ea83 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/TerremarkVCloudComputeService.java @@ -37,6 +37,7 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.internal.PersistNodeCredentials; import org.jclouds.compute.options.TemplateOptions; @@ -84,12 +85,12 @@ public class TerremarkVCloudComputeService extends BaseComputeService { RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, CleanupOrphanKeys cleanupOrphanKeys, - Optional imageExtension) { + Optional imageExtension, Optional securityGroupExtension) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, resumeNodeStrategy, suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory, - persistNodeCredentials, timeouts, userExecutor, imageExtension); + persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension); this.cleanupOrphanKeys = cleanupOrphanKeys; } diff --git a/compute/src/main/java/org/jclouds/compute/ComputeService.java b/compute/src/main/java/org/jclouds/compute/ComputeService.java index 610090a346..a7c2519c04 100644 --- a/compute/src/main/java/org/jclouds/compute/ComputeService.java +++ b/compute/src/main/java/org/jclouds/compute/ComputeService.java @@ -27,6 +27,7 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.options.TemplateOptions; @@ -383,5 +384,13 @@ public interface ComputeService { @Beta Optional getImageExtension(); + /** + * Returns the {@link SecurityGroupExtension} for this provider if it implements it. + * + * @return an optional of the {@link SecurityGroupExtension} or {@link Optional#absent()} if not + * implemented + */ + @Beta + Optional getSecurityGroupExtension(); } diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java index 7581e86afe..7307196066 100644 --- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java +++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java @@ -44,6 +44,7 @@ import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.functions.CreateSshClientOncePortIsListeningOnNode; import org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials; import org.jclouds.compute.functions.TemplateOptionsToStatement; @@ -306,5 +307,11 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { return Optional.absent(); } + @Provides + @Singleton + protected Optional provideSecurityGroupExtension(Injector i){ + return Optional.absent(); + } + } diff --git a/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java b/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java index 26df759feb..8e2c75c7a8 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java +++ b/compute/src/main/java/org/jclouds/compute/domain/ComputeType.java @@ -22,6 +22,6 @@ package org.jclouds.compute.domain; */ public enum ComputeType { - NODE, IMAGE, HARDWARE; + NODE, IMAGE, HARDWARE, SECURITYGROUP; } diff --git a/compute/src/main/java/org/jclouds/compute/domain/SecurityGroup.java b/compute/src/main/java/org/jclouds/compute/domain/SecurityGroup.java new file mode 100644 index 0000000000..fecba5ad11 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/domain/SecurityGroup.java @@ -0,0 +1,80 @@ +/* + * 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.compute.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; +import java.util.Map; +import java.util.Set; + +import org.jclouds.compute.domain.ComputeType; +import org.jclouds.compute.domain.internal.ComputeMetadataImpl; +import org.jclouds.domain.Location; +import org.jclouds.domain.ResourceMetadata; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.net.domain.IpPermission; + +import com.google.common.base.Objects.ToStringHelper; +import com.google.common.base.Predicate; +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableSet; + +/** + * Describes a security group containing a set of @{link IpPermission}s + * + * @author Andrew Bayer + */ +public class SecurityGroup extends ComputeMetadataImpl { + + private final Set ipPermissions; + private final String ownerId; + + public SecurityGroup(String providerId, String name, String id, @Nullable Location location, URI uri, + Map userMetadata, Set tags, + Iterable ipPermissions, + @Nullable String ownerId) { + super(ComputeType.SECURITYGROUP, providerId, name, id, location, uri, userMetadata, tags); + this.ipPermissions = ImmutableSet.copyOf(checkNotNull(ipPermissions, "ipPermissions")); + this.ownerId = ownerId; + } + + /** + * + * @return The set of @{link IpPermission}s for this security group + */ + public Set getIpPermissions() { + return ipPermissions; + } + + /** + * + * @return the owner ID. Can be null. + */ + public String getOwnerId() { + return ownerId; + } + + @Override + protected ToStringHelper string() { + ToStringHelper helper = computeToStringPrefix(); + if (ipPermissions.size() > 0) + helper.add("ipPermissions", ipPermissions); + return addComputeToStringSuffix(helper); + } + +} diff --git a/compute/src/main/java/org/jclouds/compute/domain/SecurityGroupBuilder.java b/compute/src/main/java/org/jclouds/compute/domain/SecurityGroupBuilder.java new file mode 100644 index 0000000000..002116f6e1 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/domain/SecurityGroupBuilder.java @@ -0,0 +1,122 @@ +/* + * 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.compute.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; +import java.util.Map; +import java.util.Set; + +import org.jclouds.domain.Location; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.net.domain.IpPermission; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +/** + * @author Andrew Bayer + */ +public class SecurityGroupBuilder extends ComputeMetadataBuilder { + private ImmutableSet.Builder ipPermissions = ImmutableSet. builder(); + private String ownerId; + + public SecurityGroupBuilder() { + super(ComputeType.SECURITYGROUP); + } + + public SecurityGroupBuilder ipPermissions() { + this.ipPermissions = ImmutableSet. builder(); + return this; + } + + public SecurityGroupBuilder ipPermissions(Iterable ipPermissions) { + this.ipPermissions.addAll(checkNotNull(ipPermissions, "ipPermissions")); + return this; + } + + public SecurityGroupBuilder ipPermission(IpPermission ipPermission) { + this.ipPermissions.add(checkNotNull(ipPermission, "ipPermission")); + return this; + } + + public SecurityGroupBuilder ownerId(String ownerId) { + this.ownerId = ownerId; + return this; + } + + @Override + public SecurityGroupBuilder id(String id) { + return SecurityGroupBuilder.class.cast(super.id(id)); + } + + @Override + public SecurityGroupBuilder tags(Iterable tags) { + return SecurityGroupBuilder.class.cast(super.tags(tags)); + } + + @Override + public SecurityGroupBuilder ids(String id) { + return SecurityGroupBuilder.class.cast(super.ids(id)); + } + + @Override + public SecurityGroupBuilder providerId(String providerId) { + return SecurityGroupBuilder.class.cast(super.providerId(providerId)); + } + + @Override + public SecurityGroupBuilder name(String name) { + return SecurityGroupBuilder.class.cast(super.name(name)); + } + + @Override + public SecurityGroupBuilder location(Location location) { + return SecurityGroupBuilder.class.cast(super.location(location)); + } + + @Override + public SecurityGroupBuilder uri(URI uri) { + return SecurityGroupBuilder.class.cast(super.uri(uri)); + } + + @Override + public SecurityGroupBuilder userMetadata(Map userMetadata) { + return SecurityGroupBuilder.class.cast(super.userMetadata(userMetadata)); + } + + @Override + public SecurityGroup build() { + return new SecurityGroup(providerId, name, id, location, uri, userMetadata, tags, + ipPermissions.build(), ownerId); + } + + public static SecurityGroupBuilder fromSecurityGroup(SecurityGroup group) { + return new SecurityGroupBuilder().providerId(group.getProviderId()) + .name(group.getName()) + .id(group.getId()) + .location(group.getLocation()) + .uri(group.getUri()) + .userMetadata(group.getUserMetadata()) + .tags(group.getTags()) + .ipPermissions(group.getIpPermissions()) + .ownerId(group.getOwnerId()); + } + +} diff --git a/compute/src/main/java/org/jclouds/compute/extensions/SecurityGroupExtension.java b/compute/src/main/java/org/jclouds/compute/extensions/SecurityGroupExtension.java new file mode 100644 index 0000000000..f07e72d44b --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/extensions/SecurityGroupExtension.java @@ -0,0 +1,180 @@ +/* + * 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.compute.extensions; + +import java.util.Set; + +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.domain.Location; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; + +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation + * is optional by providers. + * + * @author Andrew Bayer + */ +public interface SecurityGroupExtension { + + /** + * List security groups. + * + * @return The set of @{link SecurityGroup}s we have access to. + */ + Set listSecurityGroups(); + + /** + * List security groups in a given @{link Location}. + * + * @return The set of @{link SecurityGroup}s we have access to in the given location. + */ + Set listSecurityGroupsInLocation(Location location); + + /** + * List security groups for a given instance given the instance's ID. + * + * @return The set of @{link SecurityGroup}s for the given instance.. + */ + Set listSecurityGroupsForNode(String id); + + /** + * Get a security group by id. + * + * @return The @{link SecurityGroup}, if it exists. + */ + SecurityGroup getSecurityGroupById(String id); + + /** + * Create a new @{link SecurityGroup} from the parameters given. + * + * @param name + * The name of the security group + * @param location + * The @{link Location} of the security group + * + * @return The SecurityGroup that has been created. + */ + SecurityGroup createSecurityGroup(String name, Location location); + + /** + * Remove an existing @{link SecurityGroup}, and its permissions. + * + * @param id + * The id of the SecurityGroup to delete. + * + * @return true if we were able to remove the group, false otherwise. + */ + boolean removeSecurityGroup(String id); + + /** + * Add a @{link IpPermission} to an existing @{link SecurityGroup}. Applies the permission to the + * security group on the provider. + * + * @param rule + * The IpPermission to add. + * @param group + * The SecurityGroup to add the permission to. + * + * @return The SecurityGroup with the new permission added, after the permission has been applied on the provider. + */ + SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group); + + /** + * Remove a @{link IpPermission} from an existing @{link SecurityGroup}. Removes the permission from the + * security group on the provider. + * + * @param rule + * The IpPermission to remove. + * @param group + * The SecurityGroup to remove the permission from. + * + * @return The SecurityGroup with the permission removed, after the permission has been removed on the provider. + */ + SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group); + + /** + * Add a @{link IpPermission} to an existing @{link SecurityGroup}, based on the parameters given. + * Applies the permission to the security group on the provider. + * + * @param protocol + * The @{link IpProtocol} for the permission. + * @param startPort + * The first port in the range to be opened, or -1 for ICMP. + * @param endPort + * The last port in the range to be opened, or -1 for ICMP. + * @param tenantIdGroupNamePairs + * source of traffic allowed is on basis of another group in a tenant, as opposed to by cidr + * @param ipRanges + * An Iterable of Strings representing the IP range(s) the permission should allow. + * @param groupIds + * An Iterable of @{link SecurityGroup} IDs this permission should allow. + * @param group + * The SecurityGroup to add the permission to. + * + * @return The SecurityGroup with the new permission added, after the permission has been applied on the provider. + */ + SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort, + Multimap tenantIdGroupNamePairs, + Iterable ipRanges, + Iterable groupIds, SecurityGroup group); + + /** + * Remove a @{link IpPermission} from an existing @{link SecurityGroup}, based on the parameters given. + * Removes the permission from the security group on the provider. + * + * @param protocol + * The @{link IpProtocol} for the permission. + * @param startPort + * The first port in the range to be opened, or -1 for ICMP. + * @param endPort + * The last port in the range to be opened, or -1 for ICMP. + * @param tenantIdGroupNamePairs + * source of traffic allowed is on basis of another group in a tenant, as opposed to by cidr + * @param ipRanges + * An Iterable of Strings representing the IP range(s) the permission should allow. + * @param groupIds + * An Iterable of @{link SecurityGroup} IDs this permission should allow. + * @param group + * The SecurityGroup to remove the permission from. + * + * @return The SecurityGroup with the permission removed, after the permission has been removed from the provider. + */ + SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort, + Multimap tenantIdGroupNamePairs, + Iterable ipRanges, + Iterable groupIds, SecurityGroup group); + + /** + * Returns true if this SecurityGroupExtension supports tenant ID + group name pairs. + */ + boolean supportsTenantIdGroupNamePairs(); + + /** + * Returns true if this SecurityGroupExtension supports group IDs. + */ + boolean supportsGroupIds(); + + /** + * Returns true if this SecurityGroupExtension supports port ranges for group authorization. + */ + boolean supportsPortRangesForGroups(); + +} diff --git a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java index fd177accad..75ea787f5a 100644 --- a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java +++ b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java @@ -66,6 +66,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; @@ -141,6 +142,7 @@ public class BaseComputeService implements ComputeService { private final RunScriptOnNode.Factory runScriptOnNodeFactory; private final ListeningExecutorService userExecutor; private final Optional imageExtension; + private final Optional securityGroupExtension; @Inject protected BaseComputeService(ComputeServiceContext context, Map credentialStore, @@ -158,7 +160,7 @@ public class BaseComputeService implements ComputeService { InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess, RunScriptOnNode.Factory runScriptOnNodeFactory, PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, - Optional imageExtension) { + Optional imageExtension, Optional securityGroupExtension) { this.context = checkNotNull(context, "context"); this.credentialStore = checkNotNull(credentialStore, "credentialStore"); this.images = checkNotNull(images, "images"); @@ -184,6 +186,7 @@ public class BaseComputeService implements ComputeService { this.persistNodeCredentials = checkNotNull(persistNodeCredentials, "persistNodeCredentials"); this.userExecutor = checkNotNull(userExecutor, "userExecutor"); this.imageExtension = checkNotNull(imageExtension, "imageExtension"); + this.securityGroupExtension = checkNotNull(securityGroupExtension, "securityGroupExtension"); } /** @@ -714,4 +717,11 @@ public class BaseComputeService implements ComputeService { return imageExtension; } + /** + * {@inheritDoc} + */ + @Override + public Optional getSecurityGroupExtension() { + return securityGroupExtension; + } } diff --git a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java index 270633a35e..d17a6cd04f 100644 --- a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java +++ b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java @@ -70,6 +70,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable { to.inboundPorts(this.getInboundPorts()); if (this.getRunScript() != null) to.runScript(this.getRunScript()); + if (this.getGroups().size() > 0) + to.securityGroups(this.getGroups()); if (this.getPrivateKey() != null) to.installPrivateKey(this.getPrivateKey()); if (this.getPublicKey() != null) @@ -294,6 +296,21 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable { throw new IllegalArgumentException("tags are immutable"); } + @Override + public Set getGroups() { + return delegate.getGroups(); + } + + @Override + public TemplateOptions securityGroups(Iterable securityGroups) { + throw new IllegalArgumentException("tags are immutable"); + } + + @Override + public TemplateOptions securityGroups(String... securityGroups) { + throw new IllegalArgumentException("tags are immutable"); + } + @Override public TemplateOptions userMetadata(Map userMetadata) { throw new IllegalArgumentException("userMetadata is immutable"); @@ -321,6 +338,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable { protected Set tags = ImmutableSet.of(); + protected Set securityGroups = ImmutableSet.of(); + protected String privateKey; protected String publicKey; @@ -339,13 +358,13 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable { return super.equals(that) && equal(this.inboundPorts, that.inboundPorts) && equal(this.script, that.script) && equal(this.publicKey, that.publicKey) && equal(this.privateKey, that.privateKey) && equal(this.blockUntilRunning, that.blockUntilRunning) && equal(this.tags, that.tags) - && equal(this.userMetadata, that.userMetadata); + && equal(this.securityGroups, that.securityGroups) && equal(this.userMetadata, that.userMetadata); } @Override public int hashCode() { return Objects.hashCode(super.hashCode(), inboundPorts, script, publicKey, privateKey, blockUntilRunning, tags, - userMetadata); + securityGroups, userMetadata); } @Override @@ -363,6 +382,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable { toString.add("blockUntilRunning", blockUntilRunning); if (tags.size() != 0) toString.add("tags", tags); + if (securityGroups.size() != 0) + toString.add("securityGroups", securityGroups); if (userMetadata.size() != 0) toString.add("userMetadata", userMetadata); return toString; @@ -380,6 +401,10 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable { return tags; } + public Set getGroups() { + return securityGroups; + } + public String getPrivateKey() { return privateKey; } @@ -450,6 +475,21 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable { return this; } + /** + * assigns the created nodes to these security groups + */ + public TemplateOptions securityGroups(Iterable securityGroups) { + this.securityGroups = ImmutableSet.copyOf(checkNotNull(securityGroups, "securityGroups")); + return this; + } + + /** + * @see TemplateOptions#securityGroups(Iterable) + */ + public TemplateOptions securityGroups(String... securityGroups) { + return securityGroups(ImmutableSet.copyOf(securityGroups)); + } + /** * Opens the set of ports to public access. */ @@ -521,6 +561,22 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable { return options.tags(tags); } + /** + * @see TemplateOptions#securityGroups + */ + public static TemplateOptions securityGroups(Iterable securityGroups) { + TemplateOptions options = new TemplateOptions(); + return options.securityGroups(securityGroups); + } + + /** + * @see TemplateOptions#securityGroups + */ + public static TemplateOptions securityGroups(String... securityGroups) { + TemplateOptions options = new TemplateOptions(); + return options.securityGroups(securityGroups); + } + /** * @see TemplateOptions#blockUntilRunning(boolean) */ diff --git a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java index 925dc1a158..22e7dcf1d7 100644 --- a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java +++ b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceAdapter.java @@ -41,13 +41,18 @@ import org.jclouds.compute.domain.NodeMetadata.Status; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; import org.jclouds.compute.domain.Template; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.predicates.ImagePredicates; import org.jclouds.domain.Location; import org.jclouds.domain.LoginCredentials; import org.jclouds.location.suppliers.all.JustProvider; import org.jclouds.rest.ResourceNotFoundException; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; @@ -55,6 +60,7 @@ import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.common.util.concurrent.ListeningExecutorService; @@ -66,20 +72,25 @@ import com.google.common.util.concurrent.ListeningExecutorService; public class StubComputeServiceAdapter implements JCloudsNativeComputeServiceAdapter { private final Supplier location; private final ConcurrentMap nodes; + private final Multimap groupsForNodes; private final ListeningExecutorService ioExecutor; private final Provider idProvider; + private final Provider groupIdProvider; private final String publicIpPrefix; private final String privateIpPrefix; private final String passwordPrefix; private final Supplier> locationSupplier; private final Map> osToVersionMap; + private final Optional securityGroupExtension; @Inject public StubComputeServiceAdapter(ConcurrentMap nodes, @Named(Constants.PROPERTY_IO_WORKER_THREADS) ListeningExecutorService ioExecutor, Supplier location, @Named("NODE_ID") Provider idProvider, @Named("PUBLIC_IP_PREFIX") String publicIpPrefix, @Named("PRIVATE_IP_PREFIX") String privateIpPrefix, @Named("PASSWORD_PREFIX") String passwordPrefix, - JustProvider locationSupplier, Map> osToVersionMap) { + JustProvider locationSupplier, Map> osToVersionMap, + Multimap groupsForNodes, @Named("GROUP_ID") Provider groupIdProvider, + Optional securityGroupExtension) { this.nodes = nodes; this.ioExecutor = ioExecutor; this.location = location; @@ -89,6 +100,9 @@ public class StubComputeServiceAdapter implements JCloudsNativeComputeServiceAda this.passwordPrefix = passwordPrefix; this.locationSupplier = locationSupplier; this.osToVersionMap = osToVersionMap; + this.groupsForNodes = groupsForNodes; + this.groupIdProvider = groupIdProvider; + this.securityGroupExtension = securityGroupExtension; } protected void setStateOnNode(Status status, NodeMetadata node) { @@ -133,6 +147,22 @@ public class StubComputeServiceAdapter implements JCloudsNativeComputeServiceAda builder.credentials(LoginCredentials.builder().user("root").password(passwordPrefix + id).build()); NodeMetadata node = builder.build(); nodes.put(node.getId(), node); + + if (template.getOptions().getGroups().size() > 0) { + final String groupId = Iterables.getFirst(template.getOptions().getGroups(), "0"); + Optional secGroup = Iterables.tryFind(securityGroupExtension.get().listSecurityGroups(), + new Predicate() { + @Override + public boolean apply(SecurityGroup input) { + return input.getId().equals(groupId); + } + }); + + if (secGroup.isPresent()) { + groupsForNodes.put(node.getId(), secGroup.get()); + } + } + setStateOnNodeAfterDelay(Status.RUNNING, node, 100); return new NodeWithInitialCredentials(node); } @@ -195,6 +225,8 @@ public class StubComputeServiceAdapter implements JCloudsNativeComputeServiceAda return; setStateOnNodeAfterDelay(Status.PENDING, node, 0); setStateOnNodeAfterDelay(Status.TERMINATED, node, 50); + groupsForNodes.removeAll(id); + ioExecutor.execute(new Runnable() { @Override diff --git a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java index 91e858af06..ab381b3bb0 100644 --- a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java +++ b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java @@ -17,8 +17,12 @@ package org.jclouds.compute.stub.config; import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.concurrent.SingleThreaded; +import com.google.common.base.Optional; +import com.google.inject.Injector; + /** * * @author Adrian Cole @@ -35,4 +39,10 @@ public class StubComputeServiceContextModule extends JCloudsNativeComputeService install(new StubComputeServiceDependenciesModule()); super.configure(); } + + @Override + protected Optional provideSecurityGroupExtension(Injector i) { + return Optional.of(i.getInstance(SecurityGroupExtension.class)); + } + } diff --git a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java index de28ee142d..0d07bbebbc 100644 --- a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java +++ b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceDependenciesModule.java @@ -29,8 +29,11 @@ import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata.Status; import org.jclouds.compute.domain.Processor; +import org.jclouds.compute.domain.SecurityGroup; import org.jclouds.compute.domain.Volume; import org.jclouds.compute.domain.internal.VolumeImpl; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.stub.extensions.StubSecurityGroupExtension; import org.jclouds.domain.Credentials; import org.jclouds.location.Provider; import org.jclouds.predicates.SocketOpen; @@ -40,9 +43,12 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; import com.google.common.net.HostAndPort; import com.google.inject.AbstractModule; import com.google.inject.Provides; +import com.google.inject.TypeLiteral; /** * @@ -52,6 +58,8 @@ public class StubComputeServiceDependenciesModule extends AbstractModule { @Override protected void configure() { + bind(new TypeLiteral() { + }).to(StubSecurityGroupExtension.class); } @@ -73,6 +81,40 @@ public class StubComputeServiceDependenciesModule extends AbstractModule { return backing.get(creds.get().identity); } + protected static final LoadingCache> groupBacking = CacheBuilder.newBuilder() + .build(new CacheLoader>() { + + @Override + public ConcurrentMap load(String arg0) throws Exception { + return new ConcurrentHashMap(); + } + + }); + + @Provides + @Singleton + protected ConcurrentMap provideGroups(@Provider Supplier creds) + throws ExecutionException { + return groupBacking.get(creds.get().identity); + } + + protected static final LoadingCache> groupsForNodeBacking = CacheBuilder.newBuilder() + .build(new CacheLoader>() { + + @Override + public Multimap load(String arg0) throws Exception { + return LinkedHashMultimap.create(); + } + + }); + + @Provides + @Singleton + protected Multimap provideGroupsForNode(@Provider Supplier creds) + throws ExecutionException { + return groupsForNodeBacking.get(creds.get().identity); + } + protected static final LoadingCache nodeIds = CacheBuilder.newBuilder().build( new CacheLoader() { @@ -89,6 +131,22 @@ public class StubComputeServiceDependenciesModule extends AbstractModule { return nodeIds.get(creds.get().identity).incrementAndGet(); } + protected static final LoadingCache groupIds = CacheBuilder.newBuilder().build( + new CacheLoader() { + + @Override + public AtomicInteger load(String arg0) throws Exception { + return new AtomicInteger(0); + } + + }); + + @Provides + @Named("GROUP_ID") + protected Integer provideGroupIdForIdentity(@Provider Supplier creds) throws ExecutionException { + return groupIds.get(creds.get().identity).incrementAndGet(); + } + @Singleton @Provides @Named("PUBLIC_IP_PREFIX") diff --git a/compute/src/main/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtension.java b/compute/src/main/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtension.java new file mode 100644 index 0000000000..5e8fdd91c1 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtension.java @@ -0,0 +1,256 @@ +/* + * 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.compute.stub.extensions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterables.filter; + +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; + +import org.jclouds.Constants; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.domain.Location; +import org.jclouds.location.suppliers.all.JustProvider; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; + +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; + +/** + * An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation + * is optional by providers. + * + * @author Andrew Bayer + */ +public class StubSecurityGroupExtension implements SecurityGroupExtension { + + private final Supplier location; + private final Provider groupIdProvider; + private final Supplier> locationSupplier; + private final ListeningExecutorService ioExecutor; + private final ConcurrentMap groups; + private final Multimap groupsForNodes; + + @Inject + public StubSecurityGroupExtension(ConcurrentMap groups, + @Named(Constants.PROPERTY_IO_WORKER_THREADS) ListeningExecutorService ioExecutor, + Supplier location, + @Named("GROUP_ID") Provider groupIdProvider, + JustProvider locationSupplier, + Multimap groupsForNodes) { + this.groups = groups; + this.ioExecutor = ioExecutor; + this.location = location; + this.groupIdProvider = groupIdProvider; + this.locationSupplier = locationSupplier; + this.groupsForNodes = groupsForNodes; + } + + @Override + public Set listSecurityGroups() { + return ImmutableSet.copyOf(groups.values()); + } + + @Override + public Set listSecurityGroupsInLocation(final Location location) { + return ImmutableSet.copyOf(filter(groups.values(), new Predicate() { + @Override + public boolean apply(SecurityGroup group) { + return group.getLocation().equals(location); + } + })); + } + + @Override + public Set listSecurityGroupsForNode(String nodeId) { + return ImmutableSet.copyOf(groupsForNodes.get(nodeId)); + } + + @Override + public SecurityGroup getSecurityGroupById(String id) { + return groups.get(id); + } + + @Override + public SecurityGroup createSecurityGroup(String name, Location location) { + SecurityGroupBuilder builder = new SecurityGroupBuilder(); + + String id = groupIdProvider.get() + ""; + builder.ids(id); + builder.name(name); + builder.location(location); + + SecurityGroup group = builder.build(); + + groups.put(group.getId(), group); + + return group; + } + + @Override + public boolean removeSecurityGroup(String id) { + if (groups.containsKey(id)) { + groups.remove(id); + return true; + } + return false; + } + + @Override + public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) { + SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(checkNotNull(group, "group")); + + builder.ipPermission(checkNotNull(ipPermission, "ipPermission")); + + SecurityGroup newGroup = builder.build(); + + if (groups.containsKey(newGroup.getId())) { + groups.remove(newGroup.getId()); + } + + groups.put(newGroup.getId(), newGroup); + + return newGroup; + } + + @Override + public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort, + Multimap tenantIdGroupNamePairs, + Iterable ipRanges, + Iterable groupIds, SecurityGroup group) { + IpPermission.Builder ipBuilder = IpPermission.builder(); + + ipBuilder.ipProtocol(protocol); + ipBuilder.fromPort(startPort); + ipBuilder.toPort(endPort); + if (tenantIdGroupNamePairs.size() > 0) { + ipBuilder.tenantIdGroupNamePairs(tenantIdGroupNamePairs); + } + if (Iterables.size(ipRanges) > 0) { + ipBuilder.cidrBlocks(ipRanges); + } + if (Iterables.size(groupIds) > 0) { + ipBuilder.groupIds(groupIds); + } + + IpPermission perm = ipBuilder.build(); + + SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(checkNotNull(group, "group")); + + builder.ipPermission(perm); + + SecurityGroup newGroup = builder.build(); + + if (groups.containsKey(newGroup.getId())) { + groups.remove(newGroup.getId()); + } + + groups.put(newGroup.getId(), newGroup); + + return newGroup; + } + + @Override + public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) { + SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(checkNotNull(group, "group")); + + builder.ipPermissions(); + + builder.ipPermissions(filter(group.getIpPermissions(), not(equalTo(ipPermission)))); + + SecurityGroup newGroup = builder.build(); + + if (groups.containsKey(newGroup.getId())) { + groups.remove(newGroup.getId()); + } + + groups.put(newGroup.getId(), newGroup); + + return newGroup; + } + + + @Override + public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort, + Multimap tenantIdGroupNamePairs, + Iterable ipRanges, + Iterable groupIds, SecurityGroup group) { + IpPermission.Builder ipBuilder = IpPermission.builder(); + + ipBuilder.ipProtocol(protocol); + ipBuilder.fromPort(startPort); + ipBuilder.toPort(endPort); + if (tenantIdGroupNamePairs.size() > 0) { + ipBuilder.tenantIdGroupNamePairs(tenantIdGroupNamePairs); + } + if (Iterables.size(ipRanges) > 0) { + ipBuilder.cidrBlocks(ipRanges); + } + if (Iterables.size(groupIds) > 0) { + ipBuilder.groupIds(groupIds); + } + + IpPermission perm = ipBuilder.build(); + + SecurityGroupBuilder builder = SecurityGroupBuilder.fromSecurityGroup(checkNotNull(group, "group")); + + builder.ipPermissions(); + + builder.ipPermissions(filter(group.getIpPermissions(), not(equalTo(perm)))); + + SecurityGroup newGroup = builder.build(); + + if (groups.containsKey(newGroup.getId())) { + groups.remove(newGroup.getId()); + } + + groups.put(newGroup.getId(), newGroup); + + return newGroup; + } + + @Override + public boolean supportsTenantIdGroupNamePairs() { + return false; + } + + @Override + public boolean supportsGroupIds() { + return true; + } + + @Override + public boolean supportsPortRangesForGroups() { + return true; + } +} diff --git a/compute/src/main/java/org/jclouds/net/domain/IpPermission.java b/compute/src/main/java/org/jclouds/net/domain/IpPermission.java index 895d9d3594..d04af0c216 100644 --- a/compute/src/main/java/org/jclouds/net/domain/IpPermission.java +++ b/compute/src/main/java/org/jclouds/net/domain/IpPermission.java @@ -17,11 +17,15 @@ package org.jclouds.net.domain; import static com.google.common.base.Objects.equal; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; +import static org.jclouds.util.Strings2.isCidrFormat; import java.util.Set; import com.google.common.annotations.Beta; +import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; import com.google.common.collect.ImmutableMultimap; @@ -100,6 +104,7 @@ public class IpPermission implements Comparable { * @see IpPermission#getCidrBlocks() */ public Builder cidrBlock(String cidrBlock) { + checkArgument(isCidrFormat(cidrBlock), "cidrBlock %s is not a valid CIDR", cidrBlock); this.cidrBlocks.add(cidrBlock); return this; } @@ -108,7 +113,16 @@ public class IpPermission implements Comparable { * @see IpPermission#getCidrBlocks() */ public Builder cidrBlocks(Iterable cidrBlocks) { - Iterables.addAll(this.cidrBlocks, cidrBlocks); + Iterables.addAll(this.cidrBlocks, transform(cidrBlocks, + new Function() { + @Override + public String apply(String input) { + checkArgument(isCidrFormat(input), + "input %s is not a valid CIDR", + input); + return input; + } + })); return this; } @@ -140,7 +154,7 @@ public class IpPermission implements Comparable { private final IpProtocol ipProtocol; private final Set cidrBlocks; - protected IpPermission(IpProtocol ipProtocol, int fromPort, int toPort, + public IpPermission(IpProtocol ipProtocol, int fromPort, int toPort, Multimap tenantIdGroupNamePairs, Iterable groupIds, Iterable cidrBlocks) { this.fromPort = fromPort; this.toPort = toPort; @@ -227,8 +241,8 @@ public class IpPermission implements Comparable { protected ToStringHelper string() { return Objects.toStringHelper("").add("ipProtocol", ipProtocol).add("fromPort", fromPort).add("toPort", toPort) - .add("tenantIdGroupNamePairs", tenantIdGroupNamePairs).add("groupIds", groupIds).add("groupIds", - groupIds); + .add("tenantIdGroupNamePairs", tenantIdGroupNamePairs).add("groupIds", groupIds).add("cidrBlocks", + cidrBlocks); } } diff --git a/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java b/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java index 50d44221b2..203f0f92cb 100644 --- a/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java +++ b/compute/src/main/java/org/jclouds/net/domain/IpProtocol.java @@ -29,12 +29,22 @@ public enum IpProtocol { TCP, UDP, ICMP, ALL, UNRECOGNIZED; + public String value() { + return this == ALL ? "-1" : name().toLowerCase(); + } + + @Override + public String toString() { + return value(); + } + public static IpProtocol fromValue(String protocol) { try { + if (protocol.equalsIgnoreCase("-1")) + return ALL; return valueOf(checkNotNull(protocol, "protocol").toUpperCase()); } catch (IllegalArgumentException e) { return UNRECOGNIZED; } } - } diff --git a/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseSecurityGroupExtensionLiveTest.java b/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseSecurityGroupExtensionLiveTest.java new file mode 100644 index 0000000000..1a5c6464df --- /dev/null +++ b/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseSecurityGroupExtensionLiveTest.java @@ -0,0 +1,348 @@ +/* + * 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.compute.extensions.internal; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterables.filter; +import static org.jclouds.compute.predicates.NodePredicates.inGroup; +import static org.jclouds.util.Predicates2.retry; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import javax.annotation.Resource; +import javax.inject.Named; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.domain.Location; +import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.jclouds.ssh.SshClient; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + + +/** + * Base test for {@link SecurityGroupExtension} implementations. + * + * @author David Alves + * + */ +public abstract class BaseSecurityGroupExtensionLiveTest extends BaseComputeServiceContextLiveTest { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + protected String groupId; + + /** + * Returns the template for the base node, override to test different templates. + * + * @return + */ + public Template getNodeTemplate() { + return view.getComputeService().templateBuilder().build(); + } + + + + @Test(groups = { "integration", "live" }, singleThreaded = true) + public void testCreateSecurityGroup() throws RunNodesException, InterruptedException, ExecutionException { + + ComputeService computeService = view.getComputeService(); + + Location location = getNodeTemplate().getLocation(); + + Optional securityGroupExtension = computeService.getSecurityGroupExtension(); + + assertTrue(securityGroupExtension.isPresent(), "security extension was not present"); + + SecurityGroup group = securityGroupExtension.get().createSecurityGroup("test-create-security-group", location); + + logger.info("Group created: %s", group); + + assertTrue(group.getName().contains("test-create-security-group")); + + groupId = group.getId(); + } + + @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateSecurityGroup") + public void testGetSecurityGroupById() throws RunNodesException, InterruptedException, ExecutionException { + + ComputeService computeService = view.getComputeService(); + + Location location = getNodeTemplate().getLocation(); + + Optional securityGroupExtension = computeService.getSecurityGroupExtension(); + + assertTrue(securityGroupExtension.isPresent(), "security extension was not present"); + + SecurityGroup group = securityGroupExtension.get().getSecurityGroupById(groupId); + + logger.info("Group found: %s", group); + + assertTrue(group.getName().contains("test-create-security-group")); + } + + @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testGetSecurityGroupById") + public void testAddIpPermission() { + + ComputeService computeService = view.getComputeService(); + + Optional securityGroupExtension = computeService.getSecurityGroupExtension(); + assertTrue(securityGroupExtension.isPresent(), "security group extension was not present"); + + Optional optGroup = getGroup(securityGroupExtension.get()); + + assertTrue(optGroup.isPresent()); + + SecurityGroup group = optGroup.get(); + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(IpProtocol.TCP); + builder.fromPort(10); + builder.toPort(20); + builder.cidrBlock("0.0.0.0/0"); + + IpPermission perm = builder.build(); + + SecurityGroup newGroup = securityGroupExtension.get().addIpPermission(perm, group); + + assertEquals(perm, Iterables.getOnlyElement(newGroup.getIpPermissions())); + } + + @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testAddIpPermission") + public void testRemoveIpPermission() { + + ComputeService computeService = view.getComputeService(); + + Optional securityGroupExtension = computeService.getSecurityGroupExtension(); + assertTrue(securityGroupExtension.isPresent(), "security group extension was not present"); + + Optional optGroup = getGroup(securityGroupExtension.get()); + + assertTrue(optGroup.isPresent()); + + SecurityGroup group = optGroup.get(); + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(IpProtocol.TCP); + builder.fromPort(10); + builder.toPort(20); + builder.cidrBlock("0.0.0.0/0"); + + IpPermission perm = builder.build(); + + SecurityGroup newGroup = securityGroupExtension.get().removeIpPermission(perm, group); + + assertEquals(0, Iterables.size(newGroup.getIpPermissions())); + } + + @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testRemoveIpPermission") + public void testAddIpPermissionsFromSpec() { + + ComputeService computeService = view.getComputeService(); + + Optional securityGroupExtension = computeService.getSecurityGroupExtension(); + assertTrue(securityGroupExtension.isPresent(), "security group extension was not present"); + + Optional optGroup = getGroup(securityGroupExtension.get()); + + assertTrue(optGroup.isPresent()); + + SecurityGroup group = optGroup.get(); + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(IpProtocol.TCP); + builder.fromPort(50); + builder.toPort(60); + builder.cidrBlock("0.0.0.0/0"); + + IpPermission perm = builder.build(); + + SecurityGroup newGroup = securityGroupExtension.get().addIpPermission(IpProtocol.TCP, + 50, + 60, + emptyMultimap(), + ImmutableSet.of("0.0.0.0/0"), + emptyStringSet(), + group); + + assertTrue(newGroup.getIpPermissions().contains(perm)); + + if (securityGroupExtension.get().supportsGroupIds()) { + IpPermission.Builder secondBuilder = IpPermission.builder(); + + int fromPort; + int toPort; + + if (securityGroupExtension.get().supportsPortRangesForGroups()) { + fromPort = 70; + toPort = 80; + } else { + fromPort = 1; + toPort = 65535; + } + secondBuilder.ipProtocol(IpProtocol.TCP); + secondBuilder.fromPort(fromPort); + secondBuilder.toPort(toPort); + secondBuilder.groupId(group.getId()); + + IpPermission secondPerm = secondBuilder.build(); + + SecurityGroup secondNewGroup = securityGroupExtension.get().addIpPermission(IpProtocol.TCP, + fromPort, + toPort, + emptyMultimap(), + emptyStringSet(), + ImmutableSet.of(group.getId()), + newGroup); + + assertTrue(secondNewGroup.getIpPermissions().contains(secondPerm)); + } + + if (securityGroupExtension.get().supportsTenantIdGroupNamePairs()) { + IpPermission.Builder thirdBuilder = IpPermission.builder(); + + int fromPort; + int toPort; + + if (securityGroupExtension.get().supportsPortRangesForGroups()) { + fromPort = 90; + toPort = 100; + } else { + fromPort = 1; + toPort = 65535; + } + thirdBuilder.ipProtocol(IpProtocol.TCP); + thirdBuilder.fromPort(fromPort); + thirdBuilder.toPort(toPort); + thirdBuilder.tenantIdGroupNamePair(group.getOwnerId(), group.getId()); + + IpPermission thirdPerm = thirdBuilder.build(); + + SecurityGroup thirdNewGroup = securityGroupExtension.get().addIpPermission(IpProtocol.TCP, + fromPort, + toPort, + thirdPerm.getTenantIdGroupNamePairs(), + emptyStringSet(), + emptyStringSet(), + newGroup); + + assertTrue(thirdNewGroup.getIpPermissions().contains(thirdPerm)); + } + } + + @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testAddIpPermissionsFromSpec") + public void testCreateNodeWithSecurityGroup() throws RunNodesException, InterruptedException, ExecutionException { + + ComputeService computeService = view.getComputeService(); + + Optional securityGroupExtension = computeService.getSecurityGroupExtension(); + + assertTrue(securityGroupExtension.isPresent(), "security group extension was not present"); + + Template template = view.getComputeService().templateBuilder() + .options(TemplateOptions.Builder.securityGroups(groupId)) + .build(); + + NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test-create-node-with-group", 1, template)); + + Set groups = securityGroupExtension.get().listSecurityGroupsForNode(node.getId()); + + assertTrue(groups.size() > 0, "node has no groups"); + + Optional secGroup = Iterables.tryFind(securityGroupExtension.get().listSecurityGroupsForNode(node.getId()), + new Predicate() { + @Override + public boolean apply(SecurityGroup input) { + return input.getId().equals(groupId); + } + }); + + assertTrue(secGroup.isPresent()); + + computeService.destroyNodesMatching(inGroup(node.getGroup())); + + + } + + // testDeleteSecurityGroup currently disabled until I can find a way to get it to delete the security group while a terminated + // instance is still floating around in EC2. - abayer, 6/14/13 + /* + @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateNodeWithSecurityGroup") + public void testDeleteSecurityGroup() { + + ComputeService computeService = view.getComputeService(); + + Optional securityGroupExtension = computeService.getSecurityGroupExtension(); + assertTrue(securityGroupExtension.isPresent(), "security group extension was not present"); + + Optional optGroup = getGroup(securityGroupExtension.get()); + + assertTrue(optGroup.isPresent()); + + SecurityGroup group = optGroup.get(); + + assertTrue(securityGroupExtension.get().removeSecurityGroup(group.getId())); + } + */ + + private Multimap emptyMultimap() { + return LinkedHashMultimap.create(); + } + + private Set emptyStringSet() { + return Sets.newLinkedHashSet(); + } + + private Optional getGroup(SecurityGroupExtension ext) { + return Iterables.tryFind(ext.listSecurityGroups(), new Predicate() { + @Override + public boolean apply(SecurityGroup input) { + return input.getId().equals(groupId); + } + }); + } +} diff --git a/compute/src/test/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtensionIntegrationTest.java b/compute/src/test/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtensionIntegrationTest.java new file mode 100644 index 0000000000..8c2b31c3c1 --- /dev/null +++ b/compute/src/test/java/org/jclouds/compute/stub/extensions/StubSecurityGroupExtensionIntegrationTest.java @@ -0,0 +1,70 @@ +/* + * 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.compute.extensions.internal; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.jclouds.util.Predicates2.retry; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Properties; +import java.util.concurrent.ExecutionException; + +import javax.annotation.Resource; +import javax.inject.Named; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.domain.Location; +import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.ssh.SshClient; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + + +/** + * Base test for {@link SecurityGroupExtension} implementations. + * + * @author Andrew Bayer + * + */ +@Test(groups = { "integration", "live" }, singleThreaded = true, testName="StubSecurityGroupExtensionIntegrationTest") +public class StubSecurityGroupExtensionIntegrationTest extends BaseSecurityGroupExtensionLiveTest { + + public StubSecurityGroupExtensionIntegrationTest() { + provider = "stub"; + } + + @Override + protected Properties setupProperties() { + Properties overrides = super.setupProperties(); + // This is a hack to make sure we get a different set of node IDs, nodes, groups, etc from StubComputeServiceIntegrationTest. + overrides.setProperty(provider + ".identity", "sec-stub"); + + return overrides; + } +} diff --git a/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java b/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java index 2c278f6df6..30f5453962 100644 --- a/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java +++ b/compute/src/test/java/org/jclouds/net/util/IpPermissionsTest.java @@ -37,6 +37,20 @@ public class IpPermissionsTest { .cidrBlock("0.0.0.0/0").build()); } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testAllProtocolInvalidCidr() { + IpPermissions authorization = IpPermissions.permitAnyProtocol(); + assertEquals(authorization, IpPermission.builder().ipProtocol(IpProtocol.ALL).fromPort(1).toPort(65535) + .cidrBlock("a.0.0.0/0").build()); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testAllProtocolInvalidCidrMultiple() { + IpPermissions authorization = IpPermissions.permitAnyProtocol(); + assertEquals(authorization, IpPermission.builder().ipProtocol(IpProtocol.ALL).fromPort(1).toPort(65535) + .cidrBlocks(ImmutableSet.of("a.0.0.0/0", "0.0.0.0/0")).build()); + } + public void testAllProtocolCidrBound() { IpPermissions authorization = IpPermissions.permit(IpProtocol.ALL).originatingFromCidrBlock("1.1.1.1/32"); assertEquals(authorization, IpPermission.builder().ipProtocol(IpProtocol.ALL).fromPort(1).toPort(65535) diff --git a/core/src/main/java/org/jclouds/util/Strings2.java b/core/src/main/java/org/jclouds/util/Strings2.java index 8b85b5320c..2a47286cbe 100644 --- a/core/src/main/java/org/jclouds/util/Strings2.java +++ b/core/src/main/java/org/jclouds/util/Strings2.java @@ -87,7 +87,16 @@ public class Strings2 { } } }); + + private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})"; + private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,3})"; + private static final Pattern ADDRESS_PATTERN = Pattern.compile(IP_ADDRESS); + private static final Pattern CIDR_PATTERN = Pattern.compile(SLASH_FORMAT); + public static boolean isCidrFormat(String in) { + return CIDR_PATTERN.matcher(in).matches(); + } + private static final Pattern URL_ENCODED_PATTERN = Pattern.compile(".*%[a-fA-F0-9][a-fA-F0-9].*"); public static boolean isUrlEncoded(String in) { diff --git a/core/src/test/java/org/jclouds/util/Strings2Test.java b/core/src/test/java/org/jclouds/util/Strings2Test.java index d7e3cd51b3..5b1c9a0289 100644 --- a/core/src/test/java/org/jclouds/util/Strings2Test.java +++ b/core/src/test/java/org/jclouds/util/Strings2Test.java @@ -53,4 +53,12 @@ public class Strings2Test { assertEquals(actual, urlDecode(urlEncode(actual))); } + public void testIsCidrFormat() { + assert Strings2.isCidrFormat("1.2.3.4/5"); + assert Strings2.isCidrFormat("0.0.0.0/0"); + assert !Strings2.isCidrFormat("banana"); + assert !Strings2.isCidrFormat("1.2.3.4"); + assert !Strings2.isCidrFormat("500.500.500.500/2423"); + } + } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java index d9a15f3bbb..8bcc4d58e0 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java @@ -46,6 +46,7 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.internal.PersistNodeCredentials; import org.jclouds.compute.options.TemplateOptions; @@ -104,13 +105,14 @@ public class AWSEC2ComputeService extends EC2ComputeService { @Named("PLACEMENT") LoadingCache placementGroupMap, @Named("DELETED") Predicate placementGroupDeleted, Optional imageExtension, GroupNamingConvention.Factory namingConvention, - @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames) { + @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames, + Optional securityGroupExtension) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, initScriptRunnerFactory, runScriptOnNodeFactory, initAdminAccess, persistNodeCredentials, timeouts, userExecutor, client, credentialsMap, securityGroupMap, imageExtension, - namingConvention, generateInstanceNames); + namingConvention, generateInstanceNames, securityGroupExtension); this.client = client; this.placementGroupMap = placementGroupMap; this.placementGroupDeleted = placementGroupDeleted; diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java index 7d18968183..5341d57a46 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java @@ -39,6 +39,7 @@ import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier; import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.domain.Image; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass; import org.jclouds.ec2.compute.domain.RegionAndName; @@ -173,4 +174,9 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext protected Optional provideImageExtension(Injector i) { return Optional.of(i.getInstance(ImageExtension.class)); } + + @Override + protected Optional provideSecurityGroupExtension(Injector i) { + return Optional.of(i.getInstance(SecurityGroupExtension.class)); + } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java index f82216f215..8102cb690b 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java @@ -31,6 +31,7 @@ import javax.inject.Singleton; import org.jclouds.aws.ec2.compute.AWSEC2ComputeService; import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; +import org.jclouds.aws.ec2.compute.extensions.AWSEC2SecurityGroupExtension; import org.jclouds.aws.ec2.compute.loaders.AWSEC2CreateSecurityGroupIfNeeded; import org.jclouds.aws.ec2.compute.suppliers.CallForImages; import org.jclouds.aws.ec2.domain.PlacementGroup; @@ -41,8 +42,10 @@ import org.jclouds.aws.ec2.predicates.PlacementGroupAvailable; import org.jclouds.aws.ec2.predicates.PlacementGroupDeleted; import org.jclouds.compute.ComputeService; import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.SecurityGroup; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.config.ValueOfConfigurationKeyOrNull; import org.jclouds.domain.LoginCredentials; @@ -50,9 +53,11 @@ import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule; import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.extensions.EC2ImageExtension; +import org.jclouds.ec2.compute.extensions.EC2SecurityGroupExtension; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.functions.CredentialsForInstance; import org.jclouds.ec2.compute.functions.EC2ImageParser; +import org.jclouds.ec2.compute.functions.EC2SecurityGroupToSecurityGroup; import org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstance; import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; @@ -105,8 +110,12 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep install(new FactoryModuleBuilder().build(CallForImages.Factory.class)); bind(new TypeLiteral>() { }).to(EC2ImageParser.class); + bind(new TypeLiteral>() { + }).to(EC2SecurityGroupToSecurityGroup.class); bind(new TypeLiteral() { }).to(EC2ImageExtension.class); + bind(new TypeLiteral() { + }).to(AWSEC2SecurityGroupExtension.class); } @Provides diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtension.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtension.java new file mode 100644 index 0000000000..c0e21f0da4 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtension.java @@ -0,0 +1,177 @@ +/* + * 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.aws.ec2.compute.extensions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static com.google.common.base.Predicates.notNull; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Iterables.toArray; +import static com.google.common.collect.Iterables.transform; + +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; + +import org.jclouds.Constants; +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.functions.GroupNamingConvention.Factory; +import org.jclouds.domain.Location; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; +import org.jclouds.ec2.compute.extensions.EC2SecurityGroupExtension; +import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.domain.UserIdGroupPair; +import org.jclouds.location.Region; +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.Supplier; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.UncheckedTimeoutException; + +/** + * An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation + * is optional by providers. + * + * @author Andrew Bayer + */ +public class AWSEC2SecurityGroupExtension extends EC2SecurityGroupExtension { + protected final AWSEC2Client client; + + @Inject + public AWSEC2SecurityGroupExtension(AWSEC2Client client, + @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, + @Region Supplier> regions, + Function groupConverter, + @Memoized Supplier> locations, + @Named("SECURITY") LoadingCache groupCreator, + GroupNamingConvention.Factory namingConvention) { + super(client, userExecutor, regions, groupConverter, locations, groupCreator, namingConvention); + this.client = checkNotNull(client, "client"); + } + + + @Override + public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) { + + String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation()); + String name = group.getName(); + + client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name, ipPermission); + + return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode()); + } + + @Override + public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort, + Multimap tenantIdGroupNamePairs, + Iterable ipRanges, + Iterable groupIds, SecurityGroup group) { + String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation()); + String name = group.getName(); + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(protocol); + builder.fromPort(startPort); + builder.toPort(endPort); + + if (Iterables.size(ipRanges) > 0) { + for (String cidr : ipRanges) { + builder.cidrBlock(cidr); + } + } + + if (tenantIdGroupNamePairs.size() > 0) { + for (String userId : tenantIdGroupNamePairs.keySet()) { + for (String groupName : tenantIdGroupNamePairs.get(userId)) { + builder.tenantIdGroupNamePair(userId, groupName); + } + } + } + + client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name, builder.build()); + + return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode()); + } + + @Override + public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) { + String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation()); + String name = group.getName(); + + client.getSecurityGroupServices().revokeSecurityGroupIngressInRegion(region, name, ipPermission); + + return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode()); + } + + @Override + public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort, + Multimap tenantIdGroupNamePairs, + Iterable ipRanges, + Iterable groupIds, SecurityGroup group) { + String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation()); + String name = group.getName(); + + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(protocol); + builder.fromPort(startPort); + builder.toPort(endPort); + + if (Iterables.size(ipRanges) > 0) { + for (String cidr : ipRanges) { + builder.cidrBlock(cidr); + } + } + + if (tenantIdGroupNamePairs.size() > 0) { + for (String userId : tenantIdGroupNamePairs.keySet()) { + for (String groupName : tenantIdGroupNamePairs.get(userId)) { + builder.tenantIdGroupNamePair(userId, groupName); + } + } + } + + client.getSecurityGroupServices().revokeSecurityGroupIngressInRegion(region, name, builder.build()); + + return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode()); + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java index ad14efedd4..6676ef676a 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeeded.java @@ -32,10 +32,10 @@ import org.jclouds.aws.ec2.services.AWSSecurityGroupClient; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.UserIdGroupPair; import org.jclouds.logging.Logger; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import com.google.common.base.Predicate; import com.google.common.cache.CacheLoader; @@ -95,7 +95,7 @@ public class AWSEC2CreateSecurityGroupIfNeeded extends CacheLoader requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse); + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(IpProtocol.TCP); + builder.fromPort(22); + builder.toPort(40); + builder.cidrBlock("0.0.0.0/0"); + + IpPermission perm = builder.build(); + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); + groupBuilder.id("jclouds#some-group"); + groupBuilder.providerId("sg-3c6ef654"); + groupBuilder.name("jclouds#some-group"); + groupBuilder.location(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + + SecurityGroup origGroup = groupBuilder.build(); + + SecurityGroup newGroup = extension.addIpPermission(perm, origGroup); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + + assertNotNull(newPerm); + assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); + assertEquals(22, newPerm.getFromPort()); + assertEquals(40, newPerm.getToPort()); + assertEquals(1, newPerm.getCidrBlocks().size()); + assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0")); + } + + public void testAddIpPermissionCidrFromParams() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build(); + + + HttpRequest authorizeSecurityGroupIngressRequestRange = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "AuthorizeSecurityGroupIngress") + .addFormParam("GroupId", "jclouds#some-group") + .addFormParam("IpPermissions.0.FromPort", "22") + .addFormParam("IpPermissions.0.IpProtocol", "tcp") + .addFormParam("IpPermissions.0.IpRanges.0.CidrIp", "0.0.0.0/0") + .addFormParam("IpPermissions.0.ToPort", "40") + .build()); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse); + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); + groupBuilder.id("jclouds#some-group"); + groupBuilder.providerId("sg-3c6ef654"); + groupBuilder.name("jclouds#some-group"); + groupBuilder.location(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + + SecurityGroup origGroup = groupBuilder.build(); + + SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP, + 22, + 40, + emptyMultimap(), + ImmutableSet.of("0.0.0.0/0"), + emptyStringSet(), + origGroup); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + + assertNotNull(newPerm); + assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); + assertEquals(22, newPerm.getFromPort()); + assertEquals(40, newPerm.getToPort()); + assertEquals(1, newPerm.getCidrBlocks().size()); + assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0")); + } + + public void testAddIpPermissionGroupFromIpPermission() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build(); + + + HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "AuthorizeSecurityGroupIngress") + .addFormParam("GroupId", "jclouds#some-group") + .addFormParam("IpPermissions.0.FromPort", "22") + .addFormParam("IpPermissions.0.Groups.0.GroupName", "jclouds#some-group") + .addFormParam("IpPermissions.0.Groups.0.UserId", "993194456877") + .addFormParam("IpPermissions.0.IpProtocol", "tcp") + .addFormParam("IpPermissions.0.ToPort", "40") + .build()); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse); + + IpPermission.Builder builder = IpPermission.builder(); + + builder.ipProtocol(IpProtocol.TCP); + builder.fromPort(22); + builder.toPort(40); + builder.tenantIdGroupNamePair("993194456877", "jclouds#some-group"); + + IpPermission perm = builder.build(); + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); + groupBuilder.id("jclouds#some-group"); + groupBuilder.providerId("sg-3c6ef654"); + groupBuilder.name("jclouds#some-group"); + groupBuilder.location(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + groupBuilder.ownerId("993194456877"); + + SecurityGroup origGroup = groupBuilder.build(); + + SecurityGroup newGroup = extension.addIpPermission(perm, origGroup); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + + assertNotNull(newPerm); + assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); + assertEquals(22, newPerm.getFromPort()); + assertEquals(40, newPerm.getToPort()); + assertEquals(0, newPerm.getCidrBlocks().size()); + assertEquals(1, newPerm.getTenantIdGroupNamePairs().size()); + assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId())); + assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getName())); + } + + + public void testAddIpPermissionGroupFromParams() { + HttpRequest describeSecurityGroupsSingleRequest = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSecurityGroups") + .addFormParam("GroupName.1", "jclouds#some-group").build()); + + HttpResponse describeSecurityGroupsSingleResponse = + HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build(); + + + HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = + formSigner.filter(HttpRequest.builder() + .method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "AuthorizeSecurityGroupIngress") + .addFormParam("GroupId", "jclouds#some-group") + .addFormParam("IpPermissions.0.FromPort", "22") + .addFormParam("IpPermissions.0.Groups.0.GroupName", "jclouds#some-group") + .addFormParam("IpPermissions.0.Groups.0.UserId", "993194456877") + .addFormParam("IpPermissions.0.IpProtocol", "tcp") + .addFormParam("IpPermissions.0.ToPort", "40") + .build()); + + Builder requestResponseMap = ImmutableMap. builder(); + requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); + requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); + requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); + requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); + requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); + + requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse); + + SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); + + SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); + groupBuilder.id("jclouds#some-group"); + groupBuilder.providerId("sg-3c6ef654"); + groupBuilder.name("jclouds#some-group"); + groupBuilder.ownerId("993194456877"); + groupBuilder.location(new LocationBuilder() + .scope(LocationScope.REGION) + .id(region) + .description("region") + .build()); + + SecurityGroup origGroup = groupBuilder.build(); + + ImmutableMultimap.Builder permBuilder = ImmutableMultimap.builder(); + permBuilder.put(origGroup.getOwnerId(), origGroup.getName()); + + SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP, + 22, + 40, + permBuilder.build(), + emptyStringSet(), + emptyStringSet(), + origGroup); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + + assertNotNull(newPerm); + assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); + assertEquals(22, newPerm.getFromPort()); + assertEquals(40, newPerm.getToPort()); + assertEquals(0, newPerm.getCidrBlocks().size()); + assertEquals(1, newPerm.getTenantIdGroupNamePairs().size()); + assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId())); + assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getName())); + } + + private Multimap emptyMultimap() { + return LinkedHashMultimap.create(); + } + + private Set emptyStringSet() { + return Sets.newLinkedHashSet(); + } +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/IpProtocol.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionLiveTest.java similarity index 53% rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/IpProtocol.java rename to providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionLiveTest.java index ac139779f4..9de4f24996 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/IpProtocol.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionLiveTest.java @@ -14,26 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.openstack.nova.v2_0.domain; +package org.jclouds.aws.ec2.compute.extensions; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; -public enum IpProtocol { - TCP, UDP, ICMP, UNRECOGNIZED; - public String value() { - return name().toLowerCase(); - } +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.extensions.internal.BaseSecurityGroupExtensionLiveTest; +import org.testng.annotations.Test; - @Override - public String toString() { - return value(); - } +import com.google.inject.Module; - public static IpProtocol fromValue(String protocol) { - try { - return valueOf(checkNotNull(protocol, "protocol").toUpperCase()); - } catch (IllegalArgumentException e) { - return UNRECOGNIZED; - } +/** + * Live test for aws-ec2 {@link SecurityGroupExtension} implementation + * + * @author Andrew Bayer + * + */ +@Test(groups = "live", singleThreaded = true, testName = "AWSEC2SecurityGroupExtensionLiveTest") +public class AWSEC2SecurityGroupExtensionLiveTest extends BaseSecurityGroupExtensionLiveTest { + + public AWSEC2SecurityGroupExtensionLiveTest() { + provider = "aws-ec2"; } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java index db3093c2af..269a8f09dd 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/loaders/AWSEC2CreateSecurityGroupIfNeededTest.java @@ -30,10 +30,10 @@ import java.util.concurrent.ExecutionException; import org.jclouds.aws.ec2.services.AWSSecurityGroupClient; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.domain.UserIdGroupPair; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import org.testng.annotations.Test; import com.google.common.base.Predicate; @@ -63,20 +63,20 @@ public class AWSEC2CreateSecurityGroupIfNeededTest { .fromPort(22) .toPort(22) .ipProtocol(IpProtocol.TCP) - .ipRange("0.0.0.0/0") + .cidrBlock("0.0.0.0/0") .build()); permissions.add(IpPermission.builder() .fromPort(0) .toPort(65535) .ipProtocol(IpProtocol.TCP) - .userIdGroupPair("ownerId", "group") + .tenantIdGroupNamePair("ownerId", "group") .build()); permissions.add(IpPermission.builder() .fromPort(0) .toPort(65535) .ipProtocol(IpProtocol.UDP) - .userIdGroupPair("ownerId", "group") + .tenantIdGroupNamePair("ownerId", "group") .build()); client.createSecurityGroupInRegion("region", "group", "group"); diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java index 40e49dd935..8f948f535c 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/parse/DescribeSecurityGroupsResponseTest.java @@ -25,12 +25,12 @@ import static org.testng.Assert.assertEquals; import java.io.InputStream; import java.util.Set; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.xml.BaseEC2HandlerTest; import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler; import org.jclouds.http.functions.ParseSax; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.reflect.Invocation; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.testng.annotations.Test; @@ -69,7 +69,7 @@ public class DescribeSecurityGroupsResponseTest extends BaseEC2HandlerTest { // .vpcId("vpc-99999999") .ipPermission(IpPermission.builder() .ipProtocol(IpProtocol.ALL) - .userIdGroupPair("123123123123","sg-11111111").build()) + .tenantIdGroupNamePair("123123123123","sg-11111111").build()) // .ipPermissionEgress(IpPermission.builder() // .ipProtocol(IpProtocol.ALL) // .ipRange("0.0.0.0/0").build()) diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java index 2ad34675a7..9ebead5cf0 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java @@ -22,13 +22,13 @@ import java.io.IOException; import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404; import org.jclouds.Fallbacks.VoidOnNotFoundOr404; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.util.IpPermissions; import org.jclouds.ec2.xml.DescribeSecurityGroupsResponseHandler; import org.jclouds.http.HttpRequest; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ReleasePayloadAndReturn; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import org.jclouds.rest.internal.GeneratedHttpRequest; import org.testng.annotations.Test; diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java index 0ec81dd2e1..76c7e91ef3 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupClientLiveTest.java @@ -21,11 +21,11 @@ import static org.testng.Assert.assertNotNull; import java.util.Set; -import org.jclouds.ec2.domain.IpPermission; -import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.services.SecurityGroupClientLiveTest; import org.jclouds.ec2.util.IpPermissions; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; import org.testng.annotations.Test; import com.google.common.base.Predicate; @@ -69,7 +69,7 @@ public class AWSSecurityGroupClientLiveTest extends SecurityGroupClientLiveTest assertEventually(new GroupHasPermission(client, group2Name, new Predicate() { @Override public boolean apply(IpPermission arg0) { - return arg0.getUserIdGroupPairs().equals(ImmutableMultimap.of(group.getOwnerId(), group1Name)) + return arg0.getTenantIdGroupNamePairs().equals(ImmutableMultimap.of(group.getOwnerId(), group1Name)) && arg0.getFromPort() == 80 && arg0.getToPort() == 80 && arg0.getIpProtocol() == IpProtocol.TCP; } })); diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java index 3f5935c391..8e34a5ecab 100644 --- a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java +++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/GoGridComputeService.java @@ -37,6 +37,7 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.internal.PersistNodeCredentials; import org.jclouds.compute.options.TemplateOptions; @@ -81,12 +82,12 @@ public class GoGridComputeService extends BaseComputeService { InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess, RunScriptOnNode.Factory runScriptOnNodeFactory, PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, - Optional imageExtension) { + Optional imageExtension, Optional securityGroupExtension) { super(context, credentialStore, images, hardwareProfiles, locations, listNodesStrategy, getImageStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, resumeNodeStrategy, suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory, - persistNodeCredentials, timeouts, userExecutor, imageExtension); + persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension); } /**