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 <duncan.gr...@cloudsoftcorp.com> Authored: Fri Jul 29 13:11:14 2016 +0100 Committer: Duncan Grant <duncan.gr...@cloudsoftcorp.com> 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 + * <p> + * Is based on {@link JcloudsLocationSecurityGroupCustomizer} but can be instantiated + * in yaml e.g. + * <p> + * <pre> + * {@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"} + * } + * </pre> +*/ +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<Integer> tcpPortRanges; + private RangeSet<Integer> 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<String> tcpPortRanges) { + this.tcpPortRanges = Networking.portRulesToRanges(tcpPortRanges); + } + + public void setUdpPortRanges(ImmutableList<String> 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<IpPermission> builder = ImmutableList.<IpPermission>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<IpPermission> getIpPermissions(JcloudsLocationSecurityGroupCustomizer instance, RangeSet<Integer> portRanges, IpProtocol protocol) { + List<IpPermission> ipPermissions = ImmutableList.<IpPermission>of(); + if (portRanges != null) { + ipPermissions = + FluentIterable + .from(portRanges.asRanges()) + .transform(portRangeToPermission(instance, protocol)) + .toList(); + } + return ipPermissions; + } + + private Function<Range<Integer>, IpPermission> portRangeToPermission(final JcloudsLocationSecurityGroupCustomizer instance, final IpProtocol protocol) { + return new Function<Range<Integer>, IpPermission>() { + @Nullable + @Override + public IpPermission apply(@Nullable Range<Integer> 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<Supplier<Cidr>> 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<List> 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<Cidr> { + @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