Repository: brooklyn-server
Updated Branches:
  refs/heads/master cedbd922b -> b771d300a


Adds SetLimitsCustomizer for centos/rhel


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/2ffd8281
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/2ffd8281
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/2ffd8281

Branch: refs/heads/master
Commit: 2ffd8281c05542293848d0ec1dcd4cf7472f53c1
Parents: 770ca34
Author: Aled Sage <aled.s...@gmail.com>
Authored: Mon Nov 13 16:12:29 2017 +0000
Committer: Aled Sage <aled.s...@gmail.com>
Committed: Mon Dec 11 14:48:33 2017 +0000

----------------------------------------------------------------------
 .../camp/brooklyn/AbstractYamlRebindTest.java   |  22 ++-
 .../SetLimitsCustomizerIntegrationTest.java     |  71 ++++++++
 .../camp/brooklyn/SetLimitsCustomizerTest.java  | 168 +++++++++++++++++++
 .../util/core/internal/ssh/ExecCmdAsserts.java  |   9 +
 .../entity/machine/SetLimitsCustomizer.java     | 144 ++++++++++++++++
 .../entity/machine/SetLimitsCustomizerTest.java |  25 +++
 6 files changed, 433 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2ffd8281/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
index 7653d31..e79f0a3 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
@@ -43,7 +43,6 @@ import org.apache.brooklyn.core.mgmt.rebind.RebindOptions;
 import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixture;
 import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
 import org.apache.brooklyn.util.collections.MutableMap;
-import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.stream.Streams;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -162,13 +161,11 @@ public class AbstractYamlRebindTest extends 
RebindTestFixture<StartableApplicati
     }
     
     protected Entity createAndStartApplication(String input, Map<String,?> 
startParameters) throws Exception {
-        EntitySpec<?> spec = 
-            
mgmt().getTypeRegistry().createSpecFromPlan(CampTypePlanTransformer.FORMAT, 
input, RegisteredTypeLoadingContexts.spec(Application.class), EntitySpec.class);
-        final Entity app = mgmt().getEntityManager().createEntity(spec);
-        getLogger().info("Test created app, and will now start " + app);
+        final Entity app = createApplicationUnstarted(input);
         
-        // start the app (happens automatically if we use camp to instantiate, 
but not if we use crate spec approach)
         app.invoke(Startable.START, startParameters).get();
+        getLogger().info("Test started app " + app);
+        
         return app;
     }
 
@@ -190,6 +187,19 @@ public class AbstractYamlRebindTest extends 
RebindTestFixture<StartableApplicati
         return app;
     }
 
+    protected Entity createApplicationUnstarted(String... multiLineYaml) 
throws Exception {
+        return createApplicationUnstarted(joinLines(multiLineYaml));
+    }
+    
+    protected Entity createApplicationUnstarted(String input) throws Exception 
{
+        // starting of the app happens automatically if we use camp to 
instantiate, but not if we use create spec approach.
+        EntitySpec<?> spec = 
+            
mgmt().getTypeRegistry().createSpecFromPlan(CampTypePlanTransformer.FORMAT, 
input, RegisteredTypeLoadingContexts.spec(Application.class), EntitySpec.class);
+        final Entity app = mgmt().getEntityManager().createEntity(spec);
+        getLogger().info("Test created app (unstarted) " + app);
+        return app;
+    }
+    
     protected void addCatalogItems(Iterable<String> catalogYaml) {
         addCatalogItems(joinLines(catalogYaml));
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2ffd8281/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerIntegrationTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerIntegrationTest.java
new file mode 100644
index 0000000..b62bc22
--- /dev/null
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerIntegrationTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.entity.machine.SetLimitsCustomizer;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class SetLimitsCustomizerIntegrationTest extends AbstractYamlRebindTest 
{
+
+    private File file;
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        if (file != null) file.delete();
+        super.tearDown();
+    }
+    
+    
+    // Not using the default /etc/security/limits.d file, because don't want 
to risk destroying the dev machine's config!
+    @Test(groups="Integration")
+    public void testAppendsToGivenFile() throws Exception {
+        file = File.createTempFile("testAppendsToGivenFile", ".conf");
+        
+        Entity app = createAndStartApplication(
+            "location: localhost",
+            "services:",
+            "- type: " + EmptySoftwareProcess.class.getName(),
+            "  brooklyn.config:",
+            "    provisioning.properties:",
+            "      machineCustomizers:",
+            "        - $brooklyn:object:",
+            "            type: "+SetLimitsCustomizer.class.getName(),
+            "            brooklyn.config:",
+            "              file: " + file.getAbsolutePath(),
+            "              contents:",
+            "                - my line 1",
+            "                - my line 2");
+        waitForApplicationTasks(app);
+        
+        List<String> actual = Files.readAllLines(file.toPath());
+        assertEquals(actual, ImmutableList.of("my line 1", "my line 2"), 
"actual="+actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2ffd8281/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerTest.java
new file mode 100644
index 0000000..369dfb4
--- /dev/null
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.camp.brooklyn;
+
+import static 
org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecContainsLiteral;
+import static org.testng.Assert.assertFalse;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.entity.machine.MachineEntity;
+import org.apache.brooklyn.entity.machine.SetLimitsCustomizer;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.ExecCmd;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public class SetLimitsCustomizerTest extends AbstractYamlRebindTest {
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        RecordingSshTool.clear();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        RecordingSshTool.clear();
+    }
+
+    @Test
+    public void testWritesLimits() throws Exception {
+        runAppendsToLimitsFile(false);
+    }
+    
+    @Test
+    public void testRebindBeforeStarts() throws Exception {
+        runAppendsToLimitsFile(true);
+    }
+    
+    protected void runAppendsToLimitsFile(boolean rebindBeforeStart) throws 
Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  version: 0.0.0-SNAPSHOT",
+                "  id: machine-with-ulimits",
+                "  itemType: entity",
+                "  item:",
+                "    type: " + MachineEntity.class.getName(),
+                "    brooklyn.parameters:",
+                "    - name: ulimits",
+                "      type: java.util.List",
+                "      default:",
+                "      - \"* soft nofile 16384\"",
+                "      - \"* hard nofile 16384\"",
+                "      - \"* soft nproc 16384\"",
+                "      - \"* hard nproc 16384\"",
+                "    brooklyn.config:",
+                "      provisioning.properties:",
+                "        machineCustomizers:",
+                "          - $brooklyn:object:",
+                "              type: "+SetLimitsCustomizer.class.getName(),
+                "              brooklyn.config:",
+                "                contents: $brooklyn:config(\"ulimits\")");
+
+        Entity app = createApplicationUnstarted(
+            "location:",
+            "  byon:",
+            "    hosts:",
+            "    - 240.0.0.1:1234",
+            "    sshToolClass: "+RecordingSshTool.class.getName(),
+            "    detectMachineDetails: false",
+            "services:",
+            "- type: " + MachineEntity.class.getName(),
+            "  brooklyn.config:",
+            "    onbox.base.dir.skipResolution: true",
+            "    sshMonitoring.enabled: false",
+            "    provisioning.properties:",
+            "      machineCustomizers:",
+            "        - $brooklyn:object:",
+            "            type: "+SetLimitsCustomizer.class.getName(),
+            "            brooklyn.config:",
+            "              contents:",
+            "                - \"* soft nofile 1024\"",
+            "                - \"* hard nofile 2048\"");
+        
+        if (rebindBeforeStart) {
+            app = rebind();
+        }
+        app.invoke(Startable.START, ImmutableMap.of()).get();
+        waitForApplicationTasks(app);
+        
+        RecordingSshTool tool = 
Iterables.getFirst(RecordingSshTool.getTools(), null);
+        ExecCmd execCmd = 
Iterables.getOnlyElement(RecordingSshTool.getExecCmds());
+        assertExecContainsLiteral(execCmd, "echo \"* soft nofile 1024\" | tee 
-a /etc/security/limits.d/50-brooklyn.conf");
+        assertExecContainsLiteral(execCmd, "echo \"* hard nofile 2048\" | tee 
-a /etc/security/limits.d/50-brooklyn.conf");
+        
+        // should be disconnected, so that subsequent connections will pick up 
the ulimit changes
+        assertFalse(tool.isConnected());
+    }
+    
+    @Test
+    public void runFromCatalog() throws Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  version: 0.0.0-SNAPSHOT",
+                "  id: machine-with-ulimits",
+                "  itemType: entity",
+                "  item:",
+                "    type: " + MachineEntity.class.getName(),
+                "    brooklyn.parameters:",
+                "    - name: ulimits",
+                "      type: java.util.List",
+                "      default:",
+                "      - \"* soft nofile 1024\"",
+                "      - \"* hard nofile 2048\"",
+                "    brooklyn.config:",
+                "      provisioning.properties:",
+                "        machineCustomizers:",
+                "          - $brooklyn:object:",
+                "              type: "+SetLimitsCustomizer.class.getName(),
+                "              brooklyn.config:",
+                "                contents: $brooklyn:config(\"ulimits\")");
+
+        Entity app = createAndStartApplication(
+            "location:",
+            "  byon:",
+            "    hosts:",
+            "    - 240.0.0.1:1234",
+            "    sshToolClass: "+RecordingSshTool.class.getName(),
+            "    detectMachineDetails: false",
+            "services:",
+            "- type: machine-with-ulimits",
+            "  brooklyn.config:",
+            "    onbox.base.dir.skipResolution: true");
+        waitForApplicationTasks(app);
+        
+        RecordingSshTool tool = 
Iterables.getFirst(RecordingSshTool.getTools(), null);
+        ExecCmd execCmd = 
Iterables.getOnlyElement(RecordingSshTool.getExecCmds());
+        assertExecContainsLiteral(execCmd, "echo \"* soft nofile 1024\" | tee 
-a /etc/security/limits.d/50-brooklyn.conf");
+        assertExecContainsLiteral(execCmd, "echo \"* hard nofile 2048\" | tee 
-a /etc/security/limits.d/50-brooklyn.conf");
+        
+        // should be disconnected, so that subsequent connections will pick up 
the ulimit changes
+        assertFalse(tool.isConnected());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2ffd8281/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/ExecCmdAsserts.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/ExecCmdAsserts.java
 
b/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/ExecCmdAsserts.java
index db2a191..c6d45e6 100644
--- 
a/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/ExecCmdAsserts.java
+++ 
b/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/ExecCmdAsserts.java
@@ -52,6 +52,15 @@ public class ExecCmdAsserts {
         fail(expectedCmdRegex + " not matched by any commands in " + 
actual+(errMsg != null ? "; "+errMsg : ""));
     }
 
+    public static void assertExecContainsLiteral(ExecCmd actual, String 
literal) {
+        for (String cmd : actual.commands) {
+            if (cmd.contains(literal)) {
+                return;
+            }
+        }
+        fail("No match for '"+literal+"' in "+actual);
+    }
+
     public static void assertExecsNotContains(List<? extends ExecCmd> actuals, 
List<String> expectedNotCmdRegexs) {
         for (ExecCmd actual : actuals) {
             assertExecNotContains(actual, expectedNotCmdRegexs);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2ffd8281/software/base/src/main/java/org/apache/brooklyn/entity/machine/SetLimitsCustomizer.java
----------------------------------------------------------------------
diff --git 
a/software/base/src/main/java/org/apache/brooklyn/entity/machine/SetLimitsCustomizer.java
 
b/software/base/src/main/java/org/apache/brooklyn/entity/machine/SetLimitsCustomizer.java
new file mode 100644
index 0000000..e3997ff
--- /dev/null
+++ 
b/software/base/src/main/java/org/apache/brooklyn/entity/machine/SetLimitsCustomizer.java
@@ -0,0 +1,144 @@
+/*
+ * 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.entity.machine;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.brooklyn.api.location.BasicMachineLocationCustomizer;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.objs.Configurable;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
+import 
org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.SshEffectorTaskFactory;
+import org.apache.brooklyn.core.objs.BasicConfigurableObject;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Sets the limits (such as 'nofile' and 'nproc') on an ssh'able machine. 
Currently only CentOS and RHEL are supported.
+ * <p>
+ * For example:
+ * <pre>
+ * {@code
+ * brooklyn.catalog:
+ *   ...
+ *   item:
+ *     type: org.apache.brooklyn.entity.machine.MachineEntity
+ *     brooklyn.parameters:
+ *     - name: ulimits
+ *       type: java.util.List
+ *       description: |
+ *         Contents to add to the limits config file
+ *       default:
+ *         - "* soft nofile 16384"
+ *         - "* hard nofile 16384"
+ *         - "* soft nproc 16384"
+ *         - "* hard nproc 16384"
+ *     brooklyn.config:
+ *       provisioning.properties:
+ *         machineCustomizers:
+ *           - $brooklyn:object:
+ *               type: org.apache.brooklyn.entity.machine.SetLimitsCustomizer
+ *               brooklyn.config:
+ *                 contents: $brooklyn:config("ulimits")
+ * }
+ * </pre>
+ */
+@Beta
+public class SetLimitsCustomizer extends BasicMachineLocationCustomizer 
implements Configurable {
+
+    public static final Logger log = 
LoggerFactory.getLogger(SetLimitsCustomizer.class);
+
+    public static final ConfigKey<String> FILE_NAME = 
ConfigKeys.newStringConfigKey(
+            "file",
+            "The limits conf file to append to (and to create if necessary)",
+            "/etc/security/limits.d/50-brooklyn.conf");
+
+    @SuppressWarnings("serial")
+    public static final ConfigKey<List<String>> CONTENTS = 
ConfigKeys.newConfigKey(
+            new TypeToken<List<String>>() {},
+            "contents",
+            "The contents to be appended to the limits file",
+            ImmutableList.<String>of());
+
+    private final BasicConfigurableObject.BasicConfigurationSupport config;
+
+    public SetLimitsCustomizer() {
+        config = new BasicConfigurableObject.BasicConfigurationSupport();
+    }
+
+    @Override
+    public ConfigurationSupport config() {
+        return config;
+    }
+
+    @Override
+    public <T> T getConfig(ConfigKey<T> key) {
+        return config().get(key);
+    }
+
+    @Override
+    public void customize(MachineLocation machine) {
+        if (!(machine instanceof SshMachineLocation)) {
+            throw new IllegalStateException("Machine must be a 
SshMachineLocation, but got "+machine);
+        }
+        String file = config.get(FILE_NAME);
+        List<String> contents = config.get(CONTENTS);
+        checkArgument(Strings.isNonBlank(config.get(FILE_NAME)), "File must be 
non-empty");
+        
+        log.info("SetLimitsCustomizer setting limits on "+machine+" in file 
"+file+" to: "+Joiner.on("; ").join(contents));
+
+        try {
+            List<String> cmds = new ArrayList<>();
+            for (String content : contents) {
+                cmds.add(sudo(String.format("echo \"%s\" | tee -a %s", 
content, file)));
+            }
+            exec((SshMachineLocation)machine, true, cmds.toArray(new 
String[cmds.size()]));
+        } catch (Exception e) {
+            log.info("SetLimitsCustomizer failed to set limits on "+machine+" 
(rethrowing)", e);
+            throw e;
+        }
+    }
+
+    protected ProcessTaskWrapper<Integer> exec(SshMachineLocation machine, 
boolean asRoot, String... cmds) {
+        SshEffectorTaskFactory<Integer> taskFactory = 
SshEffectorTasks.ssh(machine, 
cmds).configure(SshMachineLocation.CLOSE_CONNECTION, true);
+        if (asRoot) taskFactory.runAsRoot();
+        ProcessTaskWrapper<Integer> result = 
DynamicTasks.queue(taskFactory).block();
+        if (result.get() != 0) {
+            throw new IllegalStateException("SetLimitsCustomizer got exit code 
"+result.get()+" executing on machine "+machine
+                    +"; cmds="+Arrays.asList(cmds)+"; 
stdout="+result.getStdout()+"; stderr="+result.getStderr());
+        }
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2ffd8281/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetLimitsCustomizerTest.java
----------------------------------------------------------------------
diff --git 
a/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetLimitsCustomizerTest.java
 
b/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetLimitsCustomizerTest.java
new file mode 100644
index 0000000..fe4a91b
--- /dev/null
+++ 
b/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetLimitsCustomizerTest.java
@@ -0,0 +1,25 @@
+/*
+ * 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.entity.machine;
+
+public class SetLimitsCustomizerTest {
+
+    // See 
camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SetLimitsCustomizerTest.java;
+    // wrote test there so it can use YAML
+}

Reply via email to