Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 0C62E200B65 for ; Wed, 17 Aug 2016 11:25:31 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 0B109160A86; Wed, 17 Aug 2016 09:25:31 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 01706160A8C for ; Wed, 17 Aug 2016 11:25:29 +0200 (CEST) Received: (qmail 52738 invoked by uid 500); 17 Aug 2016 09:25:29 -0000 Mailing-List: contact commits-help@brooklyn.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@brooklyn.apache.org Delivered-To: mailing list commits@brooklyn.apache.org Received: (qmail 52694 invoked by uid 99); 17 Aug 2016 09:25:29 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 17 Aug 2016 09:25:29 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id B386DE0243; Wed, 17 Aug 2016 09:25:28 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: aledsage@apache.org To: commits@brooklyn.apache.org Date: Wed, 17 Aug 2016 09:25:28 -0000 Message-Id: <7201f74d937b4deeadba51e78e99e88f@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] brooklyn-server git commit: Add Shared security group customizer archived-at: Wed, 17 Aug 2016 09:25:31 -0000 Repository: brooklyn-server Updated Branches: refs/heads/master a36b6412d -> ceeb02207 Add Shared security group customizer Can be instantiated in yaml Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/026d397e Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/026d397e Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/026d397e Branch: refs/heads/master Commit: 026d397e7f9ee70df33c0517b104702902485bd7 Parents: 215c02c Author: Duncan Grant Authored: Fri Jul 29 13:11:14 2016 +0100 Committer: Duncan Grant Committed: Mon Aug 15 13:17:36 2016 +0100 ---------------------------------------------------------------------- .../SharedLocationSecurityGroupCustomizer.java | 185 +++++++++++++++++++ ...aredLocationSecurityGroupCustomizerTest.java | 150 +++++++++++++++ 2 files changed, 335 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/026d397e/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/SharedLocationSecurityGroupCustomizer.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/SharedLocationSecurityGroupCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/SharedLocationSecurityGroupCustomizer.java new file mode 100644 index 0000000..e393bae --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/SharedLocationSecurityGroupCustomizer.java @@ -0,0 +1,185 @@ +/* + * 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.apache.brooklyn.location.jclouds.networking; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.apache.brooklyn.location.jclouds.BasicJcloudsLocationCustomizer; +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import org.apache.brooklyn.location.jclouds.JcloudsMachineLocation; +import org.apache.brooklyn.util.net.Cidr; +import org.apache.brooklyn.util.net.Networking; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.Template; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Strings; +import com.google.common.base.Suppliers; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Range; +import com.google.common.collect.RangeSet; + +/** + * Configures a shared security group on Jclouds locations + *

+ * Is based on {@link JcloudsLocationSecurityGroupCustomizer} but can be instantiated + * in yaml e.g. + *

+ *

+ * {@code
+ * services:
+ * - type: org.apache.brooklyn.entity.software.base.EmptySoftwareProcess
+ *   brooklyn.config:
+ *     provisioning.properties:
+ *       customizers:
+ *       - $brooklyn:object:
+ *         type: org.apache.brooklyn.location.jclouds.networking.SharedLocationSecurityGroupCustomizer
+ *         object.fields: {locationName: "test", tcpPortRanges: ["900-910", "915", "22"], cidr: "82.40.153.101/24"}
+ * }
+ * 
+*/ +public class SharedLocationSecurityGroupCustomizer extends BasicJcloudsLocationCustomizer { + + private String locationName = null; + + private String cidr = null; + + /** + * We track inbound ports from the template to open them once we've created the new + * security groups + */ + private int[] inboundPorts; + + private RangeSet tcpPortRanges; + private RangeSet udpPortRanges; + + /** + * The location name is appended to the name of the shared SG - use if you need distinct shared SGs within the same location + * + * @param locationName appended to the name of the SG + */ + public void setLocationName(String locationName) { + this.locationName = locationName; + } + + /** + * Extra ports to be opened on the entities in the SG + * + * @param tcpPortRanges + */ + public void setTcpPortRanges(List tcpPortRanges) { + this.tcpPortRanges = Networking.portRulesToRanges(tcpPortRanges); + } + + public void setUdpPortRanges(ImmutableList udpPortRanges) { + this.udpPortRanges = Networking.portRulesToRanges(udpPortRanges); + } + + /** + * Set to restict the range that the ports that are to be opened can be accessed from + * + * @param cidr + */ + public void setCidr(String cidr) { + this.cidr = cidr; + } + + @Override + public void customize(JcloudsLocation location, ComputeService computeService, Template template) { + super.customize(location, computeService, template); + + inboundPorts = template.getOptions().getInboundPorts(); + + final JcloudsLocationSecurityGroupCustomizer instance = getInstance(getSharedGroupId(location)); + if (cidr != null) instance.setSshCidrSupplier(Suppliers.ofInstance(new Cidr(cidr))); + instance.customize(location, computeService, template); + } + + @Override + public void customize(JcloudsLocation location, ComputeService computeService, JcloudsMachineLocation machine) { + super.customize(location, computeService, machine); + + final JcloudsLocationSecurityGroupCustomizer instance = getInstance(getSharedGroupId(location)); + + ImmutableList.Builder builder = ImmutableList.builder(); + builder.addAll(getIpPermissions(instance, tcpPortRanges, IpProtocol.TCP)); + builder.addAll(getIpPermissions(instance, udpPortRanges, IpProtocol.UDP)); + + if (inboundPorts != null) { + for (int inboundPort : inboundPorts) { + IpPermission ipPermission = IpPermission.builder() + .fromPort(inboundPort) + .toPort(inboundPort) + .ipProtocol(IpProtocol.TCP) + .cidrBlock(instance.getBrooklynCidrBlock()) + .build(); + builder.add(ipPermission); + } + } + instance.addPermissionsToLocation(machine, builder.build()); + } + + private List getIpPermissions(JcloudsLocationSecurityGroupCustomizer instance, RangeSet portRanges, IpProtocol protocol) { + List ipPermissions = ImmutableList.of(); + if (portRanges != null) { + ipPermissions = + FluentIterable + .from(portRanges.asRanges()) + .transform(portRangeToPermission(instance, protocol)) + .toList(); + } + return ipPermissions; + } + + private Function, IpPermission> portRangeToPermission(final JcloudsLocationSecurityGroupCustomizer instance, final IpProtocol protocol) { + return new Function, IpPermission>() { + @Nullable + @Override + public IpPermission apply(@Nullable Range integerRange) { + IpPermission extraPermission = IpPermission.builder() + .fromPort(integerRange.lowerEndpoint()) + .toPort(integerRange.upperEndpoint()) + .ipProtocol(protocol) + .cidrBlock(instance.getBrooklynCidrBlock()) + .build(); + return extraPermission; + } + }; + } + + private String getSharedGroupId(JcloudsLocation location) { + return (Strings.isNullOrEmpty(locationName)) + ? location.getId() + : locationName + "-" + location.getId(); + } + + @VisibleForTesting + JcloudsLocationSecurityGroupCustomizer getInstance(String sharedGroupId) { + return JcloudsLocationSecurityGroupCustomizer.getInstance(sharedGroupId); + } +} + + + http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/026d397e/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/SharedLocationSecurityGroupCustomizerTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/SharedLocationSecurityGroupCustomizerTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/SharedLocationSecurityGroupCustomizerTest.java new file mode 100644 index 0000000..565bd7b --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/SharedLocationSecurityGroupCustomizerTest.java @@ -0,0 +1,150 @@ +/* + * 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.apache.brooklyn.location.jclouds.networking; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static org.testng.Assert.assertEquals; + +import java.util.List; + +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import org.apache.brooklyn.location.jclouds.JcloudsMachineLocation; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.net.Cidr; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Location; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; + +public class SharedLocationSecurityGroupCustomizerTest { + + TestSharedLocationSecurityGroupCustomizer customizer; + JcloudsLocationSecurityGroupCustomizer sgCustomizer; + ComputeService computeService; + Location location; + SecurityGroupExtension securityApi; + private JcloudsLocation jcloudsLocation = new JcloudsLocation(MutableMap.of("deferConstruction", true)); + private Template mockTemplate; + private TemplateOptions mockOptions; + + @BeforeMethod(alwaysRun = true) + public void setUp() { + sgCustomizer = mock(JcloudsLocationSecurityGroupCustomizer.class); + customizer = new TestSharedLocationSecurityGroupCustomizer(); + location = mock(Location.class); + securityApi = mock(SecurityGroupExtension.class); + computeService = mock(ComputeService.class, Answers.RETURNS_DEEP_STUBS.get()); + mockTemplate = mock(Template.class); + mockOptions = mock(TemplateOptions.class); + when(computeService.getSecurityGroupExtension()).thenReturn(Optional.of(securityApi)); + when(mockTemplate.getOptions()).thenReturn(mockOptions); + when(mockOptions.getInboundPorts()).thenReturn(new int[]{}); + } + + @Test + public void testLocationNameAppended() { + customizer.setLocationName("Fred"); + customizer.customize(jcloudsLocation, computeService, mockTemplate); + assertEquals(customizer.sharedGroupId, "Fred-" + jcloudsLocation.getId()); + } + + @Test + public void testCidrIsSetIfAvailable() { + // Set cidr with over specified ip range which will be fixed by Cidr object + customizer.setCidr("1.1.1.1/24"); + customizer.customize(jcloudsLocation, computeService, mockTemplate); + ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass((Class)Supplier.class); + verify(sgCustomizer).setSshCidrSupplier(argumentCaptor.capture()); + Cidr cidr = argumentCaptor.getValue().get(); + assertEquals(cidr.toString(), "1.1.1.0/24"); + } + + @Test + public void testCidrNotSetIfNotavailable() { + customizer.customize(jcloudsLocation, computeService, mockTemplate); + verify(sgCustomizer, never()).setSshCidrSupplier(any(Supplier.class)); + } + + + @Test + public void testPermissionsSetFromPortRanges() { + customizer.setTcpPortRanges(ImmutableList.of("99-100")); + when(sgCustomizer.getBrooklynCidrBlock()).thenReturn("10.10.10.10/24"); + customizer.customize(jcloudsLocation, computeService, mock(JcloudsMachineLocation.class)); + assertPermissionsAdded(99, 100, IpProtocol.TCP); + } + + @Test + public void testUdpPermissionsSetFromPortRanges() { + customizer.setUdpPortRanges(ImmutableList.of("55-78")); + when(sgCustomizer.getBrooklynCidrBlock()).thenReturn("10.10.10.10/24"); + customizer.customize(jcloudsLocation, computeService, mock(JcloudsMachineLocation.class)); + assertPermissionsAdded(55, 78, IpProtocol.UDP); + } + + @Test + public void testInboundPortsAddedToPermissions() { + when(mockOptions.getInboundPorts()).thenReturn(new int[]{5}); + when(sgCustomizer.getBrooklynCidrBlock()).thenReturn("10.10.10.10/24"); + customizer.customize(jcloudsLocation, computeService, mockTemplate); + customizer.customize(jcloudsLocation, computeService, mock(JcloudsMachineLocation.class)); + assertPermissionsAdded(5, 5, IpProtocol.TCP); + } + + private void assertPermissionsAdded(int expectedFrom, int expectedTo, IpProtocol expectedProtocol) { + ArgumentCaptor listArgumentCaptor = ArgumentCaptor.forClass(List.class); + verify(sgCustomizer).addPermissionsToLocation(any(JcloudsMachineLocation.class), listArgumentCaptor.capture()); + IpPermission ipPermission = (IpPermission) listArgumentCaptor.getValue().get(0); + assertEquals(ipPermission.getFromPort(), expectedFrom); + assertEquals(ipPermission.getToPort(), expectedTo); + assertEquals(ipPermission.getIpProtocol(), expectedProtocol); + } + + /** + * Used to skip external checks in unit tests. + */ + private static class TestCidrSupplier implements Supplier { + @Override + public Cidr get() { + return new Cidr("192.168.10.10/32"); + } + } + + public class TestSharedLocationSecurityGroupCustomizer extends SharedLocationSecurityGroupCustomizer { + public String sharedGroupId; + + @Override + JcloudsLocationSecurityGroupCustomizer getInstance(String sharedGroupId) { + this.sharedGroupId = sharedGroupId; + return sgCustomizer; + } + } +} \ No newline at end of file