summaryrefslogtreecommitdiffstats
path: root/node-admin
diff options
context:
space:
mode:
authorHåkon Hallingstad <hakon@oath.com>2018-01-08 15:36:05 +0100
committerHåkon Hallingstad <hakon@oath.com>2018-01-08 15:36:05 +0100
commita00f6847b9aed600e356ef062542580ad49990ef (patch)
treef7e2f89a1694864916c690e03af61924edcc7629 /node-admin
parent2e1f09f6999c020e4b575659d812e38083c3e24f (diff)
Add tests
And, for some reason I had to specify the Java version in yolean.
Diffstat (limited to 'node-admin')
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystem.java40
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemPath.java18
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTask.java52
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTask.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTask.java90
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemTest.java91
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTaskTest.java70
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTaskTest.java49
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/TaskTestBase.java26
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTaskTest.java73
10 files changed, 442 insertions, 71 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystem.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystem.java
index de20f0975f6..65657aae472 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystem.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystem.java
@@ -10,6 +10,7 @@ import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
@@ -36,26 +37,63 @@ public class FileSystem {
uncheck(() -> Files.createDirectory(path, attributes));
}
+ public String readUtf8File(Path path) {
+ byte[] byteContent = uncheck(() -> Files.readAllBytes(path));
+ return new String(byteContent, StandardCharsets.UTF_8);
+ }
+
public void writeUtf8File(Path path, String content, OpenOption... options) {
byte[] contentInUtf8 = content.getBytes(StandardCharsets.UTF_8);
uncheck(() -> Files.write(path, contentInUtf8, options));
}
+ private PosixFileAttributes getAttributes(Path path) {
+ return uncheck(() ->
+ Files.getFileAttributeView(path, PosixFileAttributeView.class).readAttributes());
+ }
+
+ public Set<PosixFilePermission> getPermissions(Path path) {
+ return getAttributes(path).permissions();
+ }
+
+ static void validatePermissions(String permissions) {
+ Set<PosixFilePermission> set = PosixFilePermissions.fromString(permissions);
+ String serialized = PosixFilePermissions.toString(set);
+ if (!permissions.equals(serialized)) {
+ throw new IllegalArgumentException("Bad persmissions string: " + permissions);
+ }
+ }
+
/**
* @param permissions Example: "rwxr-x---" means rwx for owner, rx for group,
* and no permissions for others.
*/
public void setPermissions(Path path, String permissions) {
- Set<PosixFilePermission> permissionSet = PosixFilePermissions.fromString(permissions);
+ Set<PosixFilePermission> permissionSet;
+ try {
+ permissionSet = PosixFilePermissions.fromString(permissions);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Failed to set permissions '" +
+ permissions + "' on path " + path, e);
+ }
+
uncheck(() -> Files.setPosixFilePermissions(path, permissionSet));
}
+ public String getOwner(Path path) {
+ return getAttributes(path).owner().getName();
+ }
+
public void setOwner(Path path, String owner) {
UserPrincipalLookupService service = path.getFileSystem().getUserPrincipalLookupService();
UserPrincipal principal = uncheck(() -> service.lookupPrincipalByName(owner));
uncheck(() -> Files.setOwner(path, principal));
}
+ public String getGroup(Path path) {
+ return getAttributes(path).group().getName();
+ }
+
public void setGroup(Path path, String group) {
UserPrincipalLookupService service = path.getFileSystem().getUserPrincipalLookupService();
GroupPrincipal principal = uncheck(() -> service.lookupPrincipalByGroupName(group));
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemPath.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemPath.java
index 6fa32ea8151..552b827a255 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemPath.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemPath.java
@@ -4,6 +4,8 @@ package com.yahoo.vespa.hosted.node.admin.io;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
/**
* Convenience class for calling FileSystem methods on a fixed Path.
@@ -30,21 +32,37 @@ public class FileSystemPath {
return this;
}
+ public String readUtf8File() {
+ return fileSystem.readUtf8File(path);
+ }
+
public FileSystemPath writeUtf8File(String content, OpenOption... options) {
fileSystem.writeUtf8File(path, content, options);
return this;
}
+ public Set<PosixFilePermission> getPermissions() {
+ return fileSystem.getPermissions(path);
+ }
+
public FileSystemPath setPermissions(String permissions) {
fileSystem.setPermissions(path, permissions);
return this;
}
+ public String getOwner() {
+ return fileSystem.getOwner(path);
+ }
+
public FileSystemPath setOwner(String owner) {
fileSystem.setOwner(path, owner);
return this;
}
+ public String getGroup() {
+ return fileSystem.getGroup(path);
+ }
+
public FileSystemPath setGroup(String group) {
fileSystem.setGroup(path, group);
return this;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTask.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTask.java
index 48ed43f2920..a2ed6a80084 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTask.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTask.java
@@ -8,44 +8,36 @@ import java.util.regex.Pattern;
public class AddYumRepoTask implements Task {
private static final Pattern REPOSITORY_ID_PATTERN = Pattern.compile("^[a-zA-Z_-]+$");
- private final Params params;
-
- public static class Params {
- private final String repositoryId; // e.g. "platform_rpms-latest"
- private final String name; // e.g. "Platform RPM Latest Repo"
- private final String baseurl;
- private final boolean enabled;
-
- public Params(String repositoryId,
- String name,
- String baseurl,
- boolean enabled) {
- this.repositoryId = repositoryId;
- this.name = name;
- this.baseurl = baseurl;
- this.enabled = enabled;
- }
- }
-
- public AddYumRepoTask(Params params) {
- validateRepositoryId(params.repositoryId);
- this.params = params;
+ private final String repositoryId; // e.g. "platform_rpms-latest"
+ private final String name; // e.g. "Platform RPM Latest Repo"
+ private final String baseurl;
+ private final boolean enabled;
+
+ public AddYumRepoTask(String repositoryId,
+ String name,
+ String baseurl,
+ boolean enabled) {
+ this.repositoryId = repositoryId;
+ this.name = name;
+ this.baseurl = baseurl;
+ this.enabled = enabled;
+ validateRepositoryId(repositoryId);
}
@Override
public boolean execute(TaskContext context) {
- Path path = Paths.get("/etc/yum.repos.d",params.repositoryId + ".repo");
+ Path path = Paths.get("/etc/yum.repos.d",repositoryId + ".repo");
if (context.getFileSystem().isRegularFile(path)) {
return false;
}
- WriteFileTask.Params writeFileParams = new WriteFileTask.Params(path, this::getRepoFileContent)
- .withUser("root")
+ WriteFileTask writeFileTask = new WriteFileTask(path, this::getRepoFileContent)
+ .withOwner("root")
.withGroup("root")
.withPermissions("rw-r--r--");
- return context.executeSubtask(new WriteFileTask(writeFileParams));
+ return context.executeSubtask(writeFileTask);
}
String getRepoFileContent() {
@@ -53,10 +45,10 @@ public class AddYumRepoTask implements Task {
"# This file was generated by node admin",
"# Do NOT modify this file by hand",
"",
- "[" + params.repositoryId + "]",
- "name=" + params.name,
- "baseurl=" + params.baseurl,
- "enabled=" + (params.enabled ? 1 : 0),
+ "[" + repositoryId + "]",
+ "name=" + name,
+ "baseurl=" + baseurl,
+ "enabled=" + (enabled ? 1 : 0),
"gpgcheck=0"
) + "\n";
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTask.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTask.java
index cf0cb36d17a..4b1ca0bd2bc 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTask.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTask.java
@@ -9,7 +9,7 @@ public class MakeDirectoryTask implements Task {
private final Path directory;
private boolean withParents = false;
- MakeDirectoryTask(Path directory) {
+ public MakeDirectoryTask(Path directory) {
this.directory = directory;
}
@@ -36,6 +36,6 @@ public class MakeDirectoryTask implements Task {
@Override
public boolean execute(TaskContext context) {
- return !makeDirectory(context.getFileSystem(), directory, withParents);
+ return makeDirectory(context.getFileSystem(), directory, withParents);
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTask.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTask.java
index 8c56c2629f1..308a7470d24 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTask.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTask.java
@@ -8,59 +8,73 @@ import java.nio.file.Path;
import java.util.Optional;
public class WriteFileTask implements Task {
- private final Params params;
+ private final Path path;
+ private final Producer<String> contentProducer;
- public static class Params {
- private final Path path;
- private final Producer<String> contentProducer;
+ private Optional<String> owner = Optional.empty();
+ private Optional<String> group = Optional.empty();
+ private Optional<String> permissions = Optional.empty();
- private Optional<String> user = Optional.empty();
- private Optional<String> group = Optional.empty();
- private Optional<String> permissions = Optional.empty();
-
- public Params(Path path, Producer<String> contentProducer) {
- this.path = path;
- this.contentProducer = contentProducer;
- }
-
- public Params withUser(String user) {
- this.user = Optional.of(user);
- return this;
- }
+ public WriteFileTask(Path path, Producer<String> contentProducer) {
+ this.path = path;
+ this.contentProducer = contentProducer;
+ }
- public Params withGroup(String group) {
- this.group = Optional.of(group);
- return this;
- }
+ public WriteFileTask withOwner(String owner) {
+ this.owner = Optional.of(owner);
+ return this;
+ }
- /**
- * @param permissions of the form "rwxr-x---".
- */
- public Params withPermissions(String permissions) {
- this.permissions = Optional.of(permissions);
- return this;
- }
+ public WriteFileTask withGroup(String group) {
+ this.group = Optional.of(group);
+ return this;
}
- public WriteFileTask(Params params) {
- this.params = params;
+ /**
+ * @param permissions of the form "rwxr-x---".
+ */
+ public WriteFileTask withPermissions(String permissions) {
+ this.permissions = Optional.of(permissions);
+ return this;
}
@Override
public boolean execute(TaskContext context) {
- final FileSystemPath path = context.getFileSystem().withPath(params.path);
- if (path.isRegularFile()) {
+ final FileSystemPath fileSystemPath = context.getFileSystem().withPath(path);
+
+ // TODO: Only return false if content, permission, etc would be unchanged.
+ if (fileSystemPath.isRegularFile()) {
return false;
}
- context.executeSubtask(new MakeDirectoryTask(params.path.getParent()).withParents());
+ context.executeSubtask(new MakeDirectoryTask(path.getParent()).withParents());
- path.writeUtf8File(params.contentProducer.call())
- .setPermissions("rw-r--r--")
- .setOwner("root")
- .setGroup("root");
+ String content = contentProducer.call();
+ fileSystemPath.writeUtf8File(content);
+ permissions.ifPresent(fileSystemPath::setPermissions);
+ owner.ifPresent(fileSystemPath::setOwner);
+ group.ifPresent(fileSystemPath::setGroup);
- // TODO: Only return true if file changed.
return true;
}
+
+ public Path getPath() {
+ return path;
+ }
+
+ public Producer<String> getContentProducer() {
+ return contentProducer;
+ }
+
+ public Optional<String> getOwner() {
+ return owner;
+ }
+
+ public Optional<String> getGroup() {
+ return group;
+ }
+
+ public Optional<String> getPermissions() {
+ return permissions;
+ }
}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemTest.java
new file mode 100644
index 00000000000..91a5ccdfb27
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemTest.java
@@ -0,0 +1,91 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.io;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class FileSystemTest {
+ @Rule
+ public final TemporaryFolder folder = new TemporaryFolder();
+
+ private Path root;
+ private Path path;
+
+ private final FileSystem fileSystem = new FileSystem();
+
+ @Before
+ public void setUp() throws Exception {
+ root = folder.getRoot().toPath();
+ path = folder.newFile().toPath();
+ }
+
+ @Test
+ public void isDirectory() throws Exception {
+ assertTrue(fileSystem.isDirectory(root));
+ assertFalse(fileSystem.isDirectory(path));
+ }
+
+ @Test
+ public void isRegularFile() throws Exception {
+ assertTrue(fileSystem.isRegularFile(path));
+ assertFalse(fileSystem.isRegularFile(root));
+ }
+
+ @Test
+ public void createDirectory() throws Exception {
+ Path dir = root.resolve("subdir");
+ fileSystem.createDirectory(dir);
+ assertTrue(fileSystem.isDirectory(dir));
+ }
+
+ @Test
+ public void utf8FileIO() throws Exception {
+ String original = "foo\nbar\n";
+ Path path = root.resolve("example.txt");
+ fileSystem.writeUtf8File(path, original);
+ String fromFile = fileSystem.readUtf8File(path);
+ assertEquals(original, fromFile);
+ }
+
+ @Test
+ public void permissions() throws Exception {
+ fileSystem.setPermissions(path, "rwxr-x---");
+ Set<PosixFilePermission> permissions = fileSystem.getPermissions(path);
+ assertTrue(permissions.contains(PosixFilePermission.OWNER_READ));
+ assertTrue(permissions.contains(PosixFilePermission.OWNER_WRITE));
+ assertTrue(permissions.contains(PosixFilePermission.OWNER_EXECUTE));
+ assertTrue(permissions.contains(PosixFilePermission.GROUP_READ));
+ assertFalse(permissions.contains(PosixFilePermission.GROUP_WRITE));
+ assertTrue(permissions.contains(PosixFilePermission.GROUP_EXECUTE));
+ assertFalse(permissions.contains(PosixFilePermission.OTHERS_READ));
+ assertFalse(permissions.contains(PosixFilePermission.OTHERS_WRITE));
+ assertFalse(permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void badPermissionsString() {
+ fileSystem.setPermissions(path, "abcdefghi");
+ }
+
+ @Test
+ public void owner() throws Exception {
+ String owner = fileSystem.getOwner(path);
+ fileSystem.setOwner(path, owner);
+ }
+
+ @Test
+ public void group() throws Exception {
+ String group = fileSystem.getGroup(path);
+ fileSystem.setGroup(path, group);
+ }
+} \ No newline at end of file
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTaskTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTaskTest.java
new file mode 100644
index 00000000000..3e444f08508
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTaskTest.java
@@ -0,0 +1,70 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.task;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class AddYumRepoTaskTest extends TaskTestBase {
+ private Path expectedPath;
+ private String expectedContent;
+ private AddYumRepoTask task;
+
+ public void setUp() {
+ String repository = "repo-id";
+ String name = "name";
+ String baseUrl = "base-url";
+ boolean enabled = true;
+ expectedContent = "# This file was generated by node admin\n" +
+ "# Do NOT modify this file by hand\n" +
+ "\n" +
+ "[repo-id]\n" +
+ "name=name\n" +
+ "baseurl=base-url\n" +
+ "enabled=1\n" +
+ "gpgcheck=0\n";
+
+ task = new AddYumRepoTask(repository, name, baseUrl, enabled);
+ expectedPath = Paths.get("/etc/yum.repos.d/" + repository + ".repo");
+ }
+
+ @Test
+ public void alreadyExistsIsNoOp() {
+ when(fileSystemMock.isRegularFile(expectedPath)).thenReturn(true);
+ assertFalse(task.execute(contextMock));
+ }
+
+ @Test
+ public void fileContent() {
+ assertEquals(expectedContent, task.getRepoFileContent());
+ }
+
+ @Test
+ public void createsFile() {
+ when(fileSystemMock.isRegularFile(expectedPath)).thenReturn(false);
+ when(contextMock.executeSubtask(any())).thenReturn(true);
+ assertTrue(task.execute(contextMock));
+
+ // Writing a file with the expected content
+ ArgumentCaptor<WriteFileTask> writeFileTaskArgumentCaptor =
+ ArgumentCaptor.forClass(WriteFileTask.class);
+ verify(contextMock, times(1))
+ .executeSubtask(writeFileTaskArgumentCaptor.capture());
+ WriteFileTask writeFileTask = writeFileTaskArgumentCaptor.getValue();
+ assertEquals(expectedPath, writeFileTask.getPath());
+ assertEquals(expectedContent, writeFileTask.getContentProducer().call());
+ assertEquals("rw-r--r--", writeFileTask.getPermissions().get());
+ assertEquals("root", writeFileTask.getOwner().get());
+ assertEquals("root", writeFileTask.getGroup().get());
+ }
+} \ No newline at end of file
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTaskTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTaskTest.java
new file mode 100644
index 00000000000..cb4d8ab677c
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTaskTest.java
@@ -0,0 +1,49 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.task;
+
+import com.yahoo.vespa.hosted.node.admin.io.FileSystem;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class MakeDirectoryTaskTest {
+ private final FileSystem fileSystem = mock(FileSystem.class);
+ private final Task.TaskContext context = mock(Task.TaskContext.class);
+ private final Path root = Paths.get("/");
+ private final Path fooDir = root.resolve("foo");
+ private final Path barDir = fooDir.resolve("bar");
+ private final MakeDirectoryTask task = new MakeDirectoryTask(barDir);
+
+ @Before
+ public void setUp() {
+ when(context.getFileSystem()).thenReturn(fileSystem);
+ }
+
+ @Test
+ public void directoryExists() {
+ when(fileSystem.isDirectory(barDir)).thenReturn(true);
+ assertFalse(task.execute(context));
+ }
+
+ @Test
+ public void withParents() {
+ when(fileSystem.isDirectory(barDir)).thenReturn(false);
+ when(fileSystem.isDirectory(fooDir)).thenReturn(false);
+ when(fileSystem.isDirectory(root)).thenReturn(true);
+ assertTrue(task.withParents().execute(context));
+
+ InOrder inOrder = inOrder(fileSystem);
+ inOrder.verify(fileSystem).createDirectory(fooDir);
+ inOrder.verify(fileSystem).createDirectory(barDir);
+ inOrder.verifyNoMoreInteractions();
+ }
+} \ No newline at end of file
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/TaskTestBase.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/TaskTestBase.java
new file mode 100644
index 00000000000..ac24ac6524e
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/TaskTestBase.java
@@ -0,0 +1,26 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.task;
+
+import com.yahoo.vespa.hosted.node.admin.io.FileSystem;
+import org.junit.Before;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+abstract class TaskTestBase {
+ protected final FileSystem fileSystemMock = mock(FileSystem.class);
+ protected final Task.TaskContext contextMock = mock(Task.TaskContext.class);
+
+ @Before
+ public void baseSetup() {
+ when(contextMock.getFileSystem()).thenReturn(fileSystemMock);
+ when(fileSystemMock.withPath(any())).thenCallRealMethod();
+ setUp();
+ }
+
+ /**
+ * Override this to set up before each test.
+ */
+ void setUp() {}
+}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTaskTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTaskTest.java
new file mode 100644
index 00000000000..2294e9fed9d
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTaskTest.java
@@ -0,0 +1,73 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.task;
+
+import org.glassfish.jersey.internal.util.Producer;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class WriteFileTaskTest extends TaskTestBase {
+ private final String content = "line1\nline2\n";
+
+ @Test
+ public void testWrite() {
+ Path path = Paths.get("foo");
+
+ @SuppressWarnings("unchecked")
+ Producer<String> contentProducer = (Producer<String>) mock(Producer.class);
+ when(contentProducer.call()).thenReturn(content);
+
+ final String permissions = "rwxr-x---";
+ final String owner = "owner";
+ final String group = "group";
+
+ WriteFileTask task = new WriteFileTask(path, contentProducer)
+ .withPermissions(permissions)
+ .withOwner(owner)
+ .withGroup(group);
+
+ when(fileSystemMock.isRegularFile(path)).thenReturn(false);
+ when(contextMock.executeSubtask(any(MakeDirectoryTask.class))).thenReturn(false);
+
+ assertTrue(task.execute(contextMock));
+
+ verify(fileSystemMock).writeUtf8File(path, content);
+ verify(fileSystemMock).setPermissions(path, permissions);
+ verify(fileSystemMock).setOwner(path, owner);
+ verify(fileSystemMock).setGroup(path, group);
+
+ // Writing a file with the expected content
+ ArgumentCaptor<WriteFileTask> writeFileTaskArgumentCaptor =
+ ArgumentCaptor.forClass(WriteFileTask.class);
+ verify(contextMock, times(1))
+ .executeSubtask(writeFileTaskArgumentCaptor.capture());
+ }
+
+ @Test
+ public void fileAlreadyExists() {
+ Path path = Paths.get("foo");
+
+ final String permissions = "rwxr-x---";
+ final String owner = "owner";
+ final String group = "group";
+
+ WriteFileTask task = new WriteFileTask(path, () -> content)
+ .withPermissions(permissions)
+ .withOwner(owner)
+ .withGroup(group);
+
+ when(fileSystemMock.isRegularFile(path)).thenReturn(true);
+
+ assertFalse(task.execute(contextMock));
+ }
+} \ No newline at end of file