summaryrefslogtreecommitdiffstats
path: root/node-admin
diff options
context:
space:
mode:
authorHÃ¥kon Hallingstad <hakon@oath.com>2018-01-23 10:01:24 +0100
committerGitHub <noreply@github.com>2018-01-23 10:01:24 +0100
commite051847d1b87c8a1f5bd0a1386946aae78b1e3ab (patch)
tree137ba7f8c2d7fdf2eac9f3396c796fdade893888 /node-admin
parent81a0bae3aa618b13eb1dc9d79b01666155fe8748 (diff)
Revert "Redefine task and its context"
Diffstat (limited to 'node-admin')
-rw-r--r--node-admin/pom.xml5
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/AdminComponent.java7
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/IdempotentTask.java21
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskComponent.java17
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java21
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystem.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java)65
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemPath.java68
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/DockerAdminComponent.java6
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminConfig.java8
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminMain.java77
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdater.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterImpl.java)17
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminStateUpdater.java17
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTask.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/AddYumRepo.java)33
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTask.java49
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/Task.java17
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTask.java80
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/package-info.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/package-info.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileWriter.java61
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java34
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/package-info.java5
-rwxr-xr-xnode-admin/src/main/sh/node-admin.sh11
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java6
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java5
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java9
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemTest.java81
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java (renamed from node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterImplTest.java)9
-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.java80
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileWriterTest.java51
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/TestFileSystem.java24
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPathTest.java65
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/AddYumRepoTest.java57
36 files changed, 650 insertions, 510 deletions
diff --git a/node-admin/pom.xml b/node-admin/pom.xml
index 7b3b787b503..983e4d3a832 100644
--- a/node-admin/pom.xml
+++ b/node-admin/pom.xml
@@ -115,11 +115,6 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>com.google.jimfs</groupId>
- <artifactId>jimfs</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
<build>
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/AdminComponent.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/AdminComponent.java
index 9888cca9c7e..9bed492bd76 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/AdminComponent.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/AdminComponent.java
@@ -1,8 +1,6 @@
// 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.component;
-import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater;
-
/**
* An AdminComponent cannot assume anything about the environment until enable()
* is called: Required YUM packages may not have been installed, services
@@ -16,11 +14,6 @@ public interface AdminComponent {
void enable();
/**
- * @return NodeAdminStateUpdater used by the REST API
- */
- NodeAdminStateUpdater getNodeAdminStateUpdater();
-
- /**
* Disable component. May be called more than once.
* Must be compatible with component deconstruct().
*/
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/IdempotentTask.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/IdempotentTask.java
deleted file mode 100644
index b6b64dbf5dd..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/IdempotentTask.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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.component;
-
-/**
- * This class is thread unsafe: All method calls MUST be exclusive and serialized.
- */
-public interface IdempotentTask {
- String name();
-
- /**
- * Execute an administrative task to converge the system towards some ideal state.
- *
- * converge() must be idempotent: it may be called any number of times, or
- * interrupted at any time e.g. by `kill -9`. The caller must ensure there is at
- * most one invocation of converge() on this instance at any given time.
- *
- * @return false if the system was already converged, i.e. converge() was a no-op.
- * @throws RuntimeException (or a subclass) if the task is unable to converge.
- */
- boolean converge(TaskContext context);
-}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskComponent.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskComponent.java
deleted file mode 100644
index c54f9ee00c8..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskComponent.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.component;
-
-import com.yahoo.component.ComponentId;
-import com.yahoo.component.chain.ChainedComponent;
-
-public abstract class TaskComponent extends ChainedComponent implements IdempotentTask {
- protected TaskComponent(ComponentId id) {
- super(id);
- }
-
- public String name() {
- return getIdString();
- }
-
- public abstract boolean converge(TaskContext context);
-}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java
deleted file mode 100644
index 9def627e87f..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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.component;
-
-import java.nio.file.FileSystem;
-import java.util.EnumSet;
-import java.util.logging.Logger;
-
-public interface TaskContext {
- enum Cloud { YAHOO, AWS }
- Cloud cloud();
-
- enum Role { TENANT_DOCKER_HOST, CONFIG_SERVER_DOCKER_HOST }
- EnumSet<Role> roles();
- default boolean hasRole(Role role) {
- return roles().contains(role);
- }
-
- FileSystem fileSystem();
-
- void logSystemModification(Logger logger, String actionDescription);
-}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystem.java
index 606f8cfb06e..c5c2df9e38e 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystem.java
@@ -1,5 +1,5 @@
// 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.util.file;
+package com.yahoo.vespa.hosted.node.admin.io;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -7,7 +7,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
-import java.nio.file.Paths;
+import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
@@ -15,48 +15,52 @@ import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;
-import java.time.Instant;
import java.util.Set;
-// @Immutable
-public class UnixPath {
- private final Path path;
-
- public UnixPath(Path path) {
- this.path = path;
+/**
+ * File system operations to be mocked in unit tests.
+ */
+public class FileSystem {
+ public FileSystemPath withPath(Path path) {
+ return new FileSystemPath(this, path);
}
- public UnixPath(String path) {
- this(Paths.get(path));
+ public boolean isDirectory(Path path) {
+ return path.toFile().isDirectory();
}
- public Path toPath() {
- return path;
+ public boolean isRegularFile(Path path) {
+ return path.toFile().isFile();
}
- public void createParents() {
- uncheck(() -> Files.createDirectories(path.getParent()));
+ public void createDirectory(Path path, FileAttribute<?>... attributes) {
+ uncheck(() -> Files.createDirectory(path, attributes));
}
- public String readUtf8File() {
+ public String readUtf8File(Path path) {
byte[] byteContent = uncheck(() -> Files.readAllBytes(path));
return new String(byteContent, StandardCharsets.UTF_8);
}
- public void writeUtf8File(String content, OpenOption... options) {
+ public void writeUtf8File(Path path, String content, OpenOption... options) {
byte[] contentInUtf8 = content.getBytes(StandardCharsets.UTF_8);
uncheck(() -> Files.write(path, contentInUtf8, options));
}
- public String getPermissions() {
- return PosixFilePermissions.toString(getAttributes().permissions());
+ private PosixFileAttributes getAttributes(Path path) {
+ return uncheck(() ->
+ Files.getFileAttributeView(path, PosixFileAttributeView.class).readAttributes());
+ }
+
+ public String getPermissions(Path path) {
+ return PosixFilePermissions.toString(getAttributes(path).permissions());
}
/**
* @param permissions Example: "rwxr-x---" means rwx for owner, rx for group,
* and no permissions for others.
*/
- public void setPermissions(String permissions) {
+ public void setPermissions(Path path, String permissions) {
Set<PosixFilePermission> permissionSet;
try {
permissionSet = PosixFilePermissions.fromString(permissions);
@@ -68,35 +72,26 @@ public class UnixPath {
uncheck(() -> Files.setPosixFilePermissions(path, permissionSet));
}
- public String getOwner() {
- return getAttributes().owner().getName();
+ public String getOwner(Path path) {
+ return getAttributes(path).owner().getName();
}
- public void setOwner(String owner) {
+ 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() {
- return getAttributes().group().getName();
+ public String getGroup(Path path) {
+ return getAttributes(path).group().getName();
}
- public void setGroup(String group) {
+ public void setGroup(Path path, String group) {
UserPrincipalLookupService service = path.getFileSystem().getUserPrincipalLookupService();
GroupPrincipal principal = uncheck(() -> service.lookupPrincipalByGroupName(group));
uncheck(() -> Files.getFileAttributeView(path, PosixFileAttributeView.class).setGroup(principal));
}
- public Instant getLastModifiedTime() {
- return uncheck(() -> Files.getLastModifiedTime(path)).toInstant();
- }
-
- private PosixFileAttributes getAttributes() {
- return uncheck(() ->
- Files.getFileAttributeView(path, PosixFileAttributeView.class).readAttributes());
- }
-
@FunctionalInterface
private interface SupplierThrowingIOException<T> {
T get() throws IOException;
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
new file mode 100644
index 00000000000..bfec341e05c
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemPath.java
@@ -0,0 +1,68 @@
+// 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 java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileAttribute;
+
+/**
+ * Convenience class for calling FileSystem methods on a fixed Path.
+ */
+public class FileSystemPath {
+ private final FileSystem fileSystem;
+ private final Path path;
+
+ FileSystemPath(FileSystem fileSystem, Path path) {
+ this.fileSystem = fileSystem;
+ this.path = path;
+ }
+
+ public boolean isDirectory() {
+ return fileSystem.isDirectory(path);
+ }
+
+ public boolean isRegularFile() {
+ return fileSystem.isRegularFile(path);
+ }
+
+ public FileSystemPath createDirectory(FileAttribute<?>... attributes) {
+ fileSystem.createDirectory(path, attributes);
+ return this;
+ }
+
+ public String readUtf8File() {
+ return fileSystem.readUtf8File(path);
+ }
+
+ public FileSystemPath writeUtf8File(String content, OpenOption... options) {
+ fileSystem.writeUtf8File(path, content, options);
+ return this;
+ }
+
+ public String 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/nodeadmin/DockerAdminComponent.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/DockerAdminComponent.java
index db0313583db..8ba93e769e5 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/DockerAdminComponent.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/DockerAdminComponent.java
@@ -18,7 +18,6 @@ import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepositoryImpl;
import com.yahoo.vespa.hosted.node.admin.orchestrator.Orchestrator;
import com.yahoo.vespa.hosted.node.admin.orchestrator.OrchestratorImpl;
-import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.node.admin.util.ConfigServerHttpRequestExecutor;
import com.yahoo.vespa.hosted.node.admin.util.Environment;
@@ -42,7 +41,7 @@ public class DockerAdminComponent implements AdminComponent {
private ConfigServerHttpRequestExecutor requestExecutor;
- private Optional<NodeAdminStateUpdaterImpl> nodeAdminStateUpdater = Optional.empty();
+ private Optional<NodeAdminStateUpdater> nodeAdminStateUpdater = Optional.empty();
public DockerAdminComponent(ConfigServerConfig configServerConfig,
NodeAdminConfig config,
@@ -109,7 +108,7 @@ public class DockerAdminComponent implements AdminComponent {
metricReceiver,
clock);
- nodeAdminStateUpdater = Optional.of(new NodeAdminStateUpdaterImpl(
+ nodeAdminStateUpdater = Optional.of(new NodeAdminStateUpdater(
nodeRepository,
orchestrator,
storageMaintainer,
@@ -134,7 +133,6 @@ public class DockerAdminComponent implements AdminComponent {
// TODO: Also stop docker
}
- @Override
public NodeAdminStateUpdater getNodeAdminStateUpdater() {
return nodeAdminStateUpdater.get();
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminConfig.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminConfig.java
index df26bc2dee5..0be31ffdbae 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminConfig.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminConfig.java
@@ -7,16 +7,18 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
public class NodeAdminConfig {
private static final ObjectMapper mapper = new ObjectMapper();
/**
- * If null, the default admin component will be used.
+ * A list of components to enable instead of the default.
*/
- @JsonProperty("main-component")
- public String mainComponent = null;
+ @JsonProperty("components")
+ public List<String> components = new ArrayList<>();
public enum Mode {
aws_tenant,
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminMain.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminMain.java
index 3e9cb239890..4ee84d12b1d 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminMain.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminMain.java
@@ -10,9 +10,11 @@ import com.yahoo.vespa.hosted.dockerapi.Docker;
import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper;
import com.yahoo.vespa.hosted.node.admin.ConfigServerConfig;
import com.yahoo.vespa.hosted.node.admin.component.AdminComponent;
-import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -22,8 +24,8 @@ import java.util.stream.Collectors;
* - It will "start" (only) the necessary components.
* - Other components MUST NOT try to start (typically in constructor) since the features
* they provide is NOT WANTED and possibly destructive, and/or the environment may be
- * incompatible. For instance, trying to contact the Docker daemon too early will be
- * fatal: the node admin may not have installed and started the docker daemon.
+ * incompatible. For instance, trying to contact the Docker daemon too early will
+ * be fatal: the node admin may not have installed and started the docker daemon.
*/
public class NodeAdminMain implements AutoCloseable {
private static final Logger logger = Logger.getLogger(NodeAdminMain.class.getName());
@@ -34,7 +36,9 @@ public class NodeAdminMain implements AutoCloseable {
private final MetricReceiverWrapper metricReceiver;
private final ClassLocking classLocking;
- private AdminComponent mainAdminComponent = null;
+ private List<AdminComponent> enabledComponents = new ArrayList<>();
+
+ private Optional<DockerAdminComponent> dockerAdmin = Optional.empty();
public NodeAdminMain(ComponentRegistry<AdminComponent> adminRegistry,
ConfigServerConfig configServerConfig,
@@ -55,46 +59,53 @@ public class NodeAdminMain implements AutoCloseable {
public void start() {
NodeAdminConfig config = getConfig();
- mainAdminComponent = selectAdminComponent(config);
- mainAdminComponent.enable();
- }
- private AdminComponent selectAdminComponent(NodeAdminConfig config) {
- if (config.mainComponent == null) {
- return new DockerAdminComponent(configServerConfig, config, docker, metricReceiver, classLocking);
- }
+ if (config.components.isEmpty()) {
+ dockerAdmin = Optional.of(new DockerAdminComponent(
+ configServerConfig, config, docker, metricReceiver, classLocking));
+ enable(dockerAdmin.get());
+ } else {
+ logger.log(LogLevel.INFO, () -> {
+ String registeredComponentsList = adminRegistry
+ .allComponentsById().keySet().stream()
+ .map(ComponentId::stringValue)
+ .collect(Collectors.joining(", "));
- logger.log(LogLevel.INFO, () -> {
- String registeredComponentsList = adminRegistry
- .allComponentsById().keySet().stream()
- .map(ComponentId::stringValue)
- .collect(Collectors.joining(", "));
-
- return String.format(
- "Components registered = '%s', enabled = '%s'",
- registeredComponentsList,
- config.mainComponent);
- });
-
- AdminComponent component = adminRegistry.getComponent(config.mainComponent);
- if (component == null) {
- throw new IllegalArgumentException("There is no component named '" +
- config.mainComponent + "'");
+ String requestedComponentsList = config.components.stream()
+ .collect(Collectors.joining(", "));
+
+ return String.format(
+ "Components registered = '%s', enabled = '%s'",
+ registeredComponentsList,
+ requestedComponentsList);
+ });
+
+ for (String componentSpecificationString : config.components) {
+ AdminComponent component =
+ adminRegistry.getComponent(componentSpecificationString);
+ if (component == null) {
+ throw new IllegalArgumentException("There is no component named '" +
+ componentSpecificationString + "'");
+ }
+ enable(component);
+ }
}
+ }
- return component;
+ private void enable(AdminComponent component) {
+ component.enable();
+ enabledComponents.add(component);
}
@Override
public void close() {
- if (mainAdminComponent != null) {
- mainAdminComponent.disable();
- mainAdminComponent = null;
+ int i = enabledComponents.size();
+ while (i --> 0) {
+ enabledComponents.remove(i).disable();
}
}
public NodeAdminStateUpdater getNodeAdminStateUpdater() {
- assert mainAdminComponent != null : "start() hasn't been called yet";
- return mainAdminComponent.getNodeAdminStateUpdater();
+ return dockerAdmin.get().getNodeAdminStateUpdater();
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdater.java
index e4e66b57186..5ec041298fb 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdater.java
@@ -12,7 +12,6 @@ import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAttributes;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.orchestrator.Orchestrator;
import com.yahoo.vespa.hosted.node.admin.orchestrator.OrchestratorException;
-import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.node.admin.util.HttpException;
import com.yahoo.vespa.hosted.provision.Node;
@@ -33,16 +32,16 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
-import static com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater.State.RESUMED;
-import static com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater.State.SUSPENDED_NODE_ADMIN;
-import static com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater.State.TRANSITIONING;
+import static com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater.State.RESUMED;
+import static com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater.State.SUSPENDED_NODE_ADMIN;
+import static com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater.State.TRANSITIONING;
/**
* Pulls information from node repository and forwards containers to run to node admin.
*
* @author dybis, stiankri
*/
-public class NodeAdminStateUpdaterImpl implements NodeAdminStateUpdater {
+public class NodeAdminStateUpdater {
static final Duration FREEZE_CONVERGENCE_TIMEOUT = Duration.ofMinutes(5);
private final AtomicBoolean terminated = new AtomicBoolean(false);
@@ -68,7 +67,7 @@ public class NodeAdminStateUpdaterImpl implements NodeAdminStateUpdater {
private Optional<ClassLock> classLock;
private Instant lastTick;
- public NodeAdminStateUpdaterImpl(
+ public NodeAdminStateUpdater(
NodeRepository nodeRepository,
Orchestrator orchestrator,
StorageMaintainer storageMaintainer,
@@ -112,7 +111,8 @@ public class NodeAdminStateUpdaterImpl implements NodeAdminStateUpdater {
return this.getClass().getSimpleName() + "@" + Integer.toString(System.identityHashCode(this));
}
- @Override
+ public enum State { TRANSITIONING, RESUMED, SUSPENDED_NODE_ADMIN, SUSPENDED}
+
public Map<String, Object> getDebugPage() {
Map<String, Object> debug = new LinkedHashMap<>();
synchronized (monitor) {
@@ -142,7 +142,6 @@ public class NodeAdminStateUpdaterImpl implements NodeAdminStateUpdater {
}
}
- @Override
public boolean setResumeStateAndCheckIfResumed(State wantedState) {
synchronized (monitor) {
if (this.wantedState != wantedState) {
@@ -310,7 +309,7 @@ public class NodeAdminStateUpdaterImpl implements NodeAdminStateUpdater {
classLocking.interrupt();
- // First we need to stop NodeAdminStateUpdaterImpl thread to make sure no new NodeAgents are spawned
+ // First we need to stop NodeAdminStateUpdater thread to make sure no new NodeAgents are spawned
signalWorkToBeDone();
specVerifierScheduler.shutdown();
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java
index a5146fcae09..9a667c29fb3 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java
@@ -7,9 +7,10 @@ import com.yahoo.concurrent.classlock.ClassLocking;
import com.yahoo.container.di.componentgraph.Provider;
import com.yahoo.vespa.hosted.dockerapi.Docker;
import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper;
-import com.yahoo.vespa.hosted.node.admin.ConfigServerConfig;
import com.yahoo.vespa.hosted.node.admin.component.AdminComponent;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminMain;
+import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
+import com.yahoo.vespa.hosted.node.admin.ConfigServerConfig;
public class NodeAdminProvider implements Provider<NodeAdminStateUpdater> {
private final NodeAdminMain nodeAdminMain;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminStateUpdater.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminStateUpdater.java
deleted file mode 100644
index 755e1301c12..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminStateUpdater.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.provider;
-
-import java.util.Map;
-
-public interface NodeAdminStateUpdater {
- enum State { TRANSITIONING, RESUMED, SUSPENDED_NODE_ADMIN, SUSPENDED}
-
- /**
- * Set the wanted state, and return whether the current state equals it.
- * Typically, this method should be called repeatedly until current state
- * has converged.
- */
- boolean setResumeStateAndCheckIfResumed(State wantedState);
-
- Map<String, Object> getDebugPage();
-}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java
index a8dcde02ca6..03217c85329 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java
@@ -6,9 +6,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.LoggingRequestHandler;
+import com.yahoo.container.logging.AccessLog;
import com.yahoo.vespa.hosted.dockerapi.metrics.DimensionMetrics;
import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper;
-import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater;
+import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
import javax.inject.Inject;
import javax.ws.rs.core.MediaType;
@@ -16,6 +17,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
+import java.util.concurrent.Executor;
import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
import static com.yahoo.jdisc.http.HttpRequest.Method.PUT;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/AddYumRepo.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTask.java
index 9ca1c0286f9..a2ed6a80084 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/AddYumRepo.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/AddYumRepoTask.java
@@ -1,13 +1,11 @@
// 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.util.yum;
-
-import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
-import com.yahoo.vespa.hosted.node.admin.task.util.file.FileWriter;
+package com.yahoo.vespa.hosted.node.admin.task;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.regex.Pattern;
-public class AddYumRepo {
+public class AddYumRepoTask implements Task {
private static final Pattern REPOSITORY_ID_PATTERN = Pattern.compile("^[a-zA-Z_-]+$");
private final String repositoryId; // e.g. "platform_rpms-latest"
@@ -15,10 +13,10 @@ public class AddYumRepo {
private final String baseurl;
private final boolean enabled;
- public AddYumRepo(String repositoryId,
- String name,
- String baseurl,
- boolean enabled) {
+ public AddYumRepoTask(String repositoryId,
+ String name,
+ String baseurl,
+ boolean enabled) {
this.repositoryId = repositoryId;
this.name = name;
this.baseurl = baseurl;
@@ -26,18 +24,23 @@ public class AddYumRepo {
validateRepositoryId(repositoryId);
}
- public boolean converge(TaskContext context) {
- Path path = context.fileSystem().getPath("/etc/yum.repos.d",repositoryId + ".repo");
+ @Override
+ public boolean execute(TaskContext context) {
+ Path path = Paths.get("/etc/yum.repos.d",repositoryId + ".repo");
+
+ if (context.getFileSystem().isRegularFile(path)) {
+ return false;
+ }
- FileWriter fileWriter = new FileWriter(path, this::getRepoFileContent)
+ WriteFileTask writeFileTask = new WriteFileTask(path, this::getRepoFileContent)
.withOwner("root")
.withGroup("root")
.withPermissions("rw-r--r--");
- return fileWriter.converge(context);
+ return context.executeSubtask(writeFileTask);
}
- private String getRepoFileContent() {
+ String getRepoFileContent() {
return String.join("\n",
"# This file was generated by node admin",
"# Do NOT modify this file by hand",
@@ -50,7 +53,7 @@ public class AddYumRepo {
) + "\n";
}
- private static void validateRepositoryId(String repositoryId) {
+ static void validateRepositoryId(String repositoryId) {
if (!REPOSITORY_ID_PATTERN.matcher(repositoryId).matches()) {
throw new IllegalArgumentException("Invalid repository ID '" + repositoryId + "'");
}
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
new file mode 100644
index 00000000000..522abb81248
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/MakeDirectoryTask.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 java.nio.file.Path;
+
+public class MakeDirectoryTask implements Task {
+ private final Path path;
+ private boolean withParents = false;
+
+ public MakeDirectoryTask(Path path) {
+ this.path = path;
+ }
+
+ public MakeDirectoryTask withParents() {
+ this.withParents = true;
+ return this;
+ }
+
+ Path getPath() {
+ return path;
+ }
+
+ boolean getWithParents() {
+ return withParents;
+ }
+
+ private boolean makeDirectory(FileSystem fileSystem,
+ Path directory,
+ boolean withParents) {
+ if (fileSystem.isDirectory(directory)) {
+ return false;
+ }
+
+ if (withParents) {
+ makeDirectory(fileSystem, directory.getParent(), withParents);
+ }
+
+ fileSystem.createDirectory(directory);
+
+ return true;
+ }
+
+ @Override
+ public boolean execute(TaskContext context) {
+ return makeDirectory(context.getFileSystem(), path, withParents);
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/Task.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/Task.java
new file mode 100644
index 00000000000..4d40ac02440
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/Task.java
@@ -0,0 +1,17 @@
+// 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;
+
+public interface Task {
+ interface TaskContext {
+ FileSystem getFileSystem();
+ boolean executeSubtask(Task task);
+ }
+
+ /**
+ * @return Returns false if task was a no-op. Used for informational purposes only.
+ * @throws RuntimeException if task could not be completed.
+ */
+ boolean execute(TaskContext context);
+}
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
new file mode 100644
index 00000000000..308a7470d24
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTask.java
@@ -0,0 +1,80 @@
+// 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.FileSystemPath;
+import org.glassfish.jersey.internal.util.Producer;
+
+import java.nio.file.Path;
+import java.util.Optional;
+
+public class WriteFileTask implements Task {
+ 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();
+
+ public WriteFileTask(Path path, Producer<String> contentProducer) {
+ this.path = path;
+ this.contentProducer = contentProducer;
+ }
+
+ public WriteFileTask withOwner(String owner) {
+ this.owner = Optional.of(owner);
+ return this;
+ }
+
+ public WriteFileTask withGroup(String group) {
+ this.group = Optional.of(group);
+ return this;
+ }
+
+ /**
+ * @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 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(path.getParent()).withParents());
+
+ String content = contentProducer.call();
+ fileSystemPath.writeUtf8File(content);
+ permissions.ifPresent(fileSystemPath::setPermissions);
+ owner.ifPresent(fileSystemPath::setOwner);
+ group.ifPresent(fileSystemPath::setGroup);
+
+ 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/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/package-info.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/package-info.java
index 3c5292982d0..433884139f6 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/package-info.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/package-info.java
@@ -1,5 +1,5 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
-package com.yahoo.vespa.hosted.node.admin.task.util.yum;
+package com.yahoo.vespa.hosted.node.admin.task;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileWriter.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileWriter.java
deleted file mode 100644
index 60a7b3482b2..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileWriter.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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.util.file;
-
-import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
-import org.glassfish.jersey.internal.util.Producer;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-public class FileWriter {
- private static final Logger logger = Logger.getLogger(FileWriter.class.getName());
-
- 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();
-
- public FileWriter(Path path, Producer<String> contentProducer) {
- this.path = path;
- this.contentProducer = contentProducer;
- }
-
- public FileWriter withOwner(String owner) {
- this.owner = Optional.of(owner);
- return this;
- }
-
- public FileWriter withGroup(String group) {
- this.group = Optional.of(group);
- return this;
- }
-
- public FileWriter withPermissions(String permissions) {
- this.permissions = Optional.of(permissions);
- return this;
- }
-
- public boolean converge(TaskContext context) {
- // TODO: Only return false if content, permission, etc would be unchanged.
- if (Files.isRegularFile(path)) {
- return false;
- }
-
- context.logSystemModification(logger,"Writing file " + path);
-
- String content = contentProducer.call();
-
- UnixPath unixPath = new UnixPath(path);
- unixPath.createParents();
- unixPath.writeUtf8File(content);
- permissions.ifPresent(unixPath::setPermissions);
- owner.ifPresent(unixPath::setOwner);
- group.ifPresent(unixPath::setGroup);
-
- return true;
- }
-}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java
deleted file mode 100644
index dee5525d42a..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/IOExceptionUtil.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.util.file;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-
-public class IOExceptionUtil {
- public static <T> void uncheck(RunnableThrowingIOException<T> runnable) {
- try {
- runnable.run();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public static <T> T uncheck(SupplierThrowingIOException<T> supplier) {
- try {
- return supplier.get();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- @FunctionalInterface
- public interface SupplierThrowingIOException<T> {
- T get() throws IOException;
- }
-
-
- @FunctionalInterface
- public interface RunnableThrowingIOException<T> {
- void run() throws IOException;
- }
-}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/package-info.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/package-info.java
deleted file mode 100644
index 076912c073d..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-@ExportPackage
-package com.yahoo.vespa.hosted.node.admin.task.util.file;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/node-admin/src/main/sh/node-admin.sh b/node-admin/src/main/sh/node-admin.sh
index 3196ff9fa32..ff0ea492318 100755
--- a/node-admin/src/main/sh/node-admin.sh
+++ b/node-admin/src/main/sh/node-admin.sh
@@ -67,19 +67,16 @@ EOF
exit 1
}
-if (( $# == 0 )); then
+if (( $# != 1 )); then
Usage
fi
-command="$1"
-shift
-
-case "$command" in
+case "$1" in
start)
- "$VESPA_HOME"/libexec/vespa/standalone-container.sh start -s node-admin -u root "$@"
+ "$VESPA_HOME"/libexec/vespa/standalone-container.sh start -s node-admin -u root
;;
stop)
- "$VESPA_HOME"/libexec/vespa/standalone-container.sh stop -s node-admin -u root "$@"
+ "$VESPA_HOME"/libexec/vespa/standalone-container.sh stop -s node-admin -u root
;;
*) Usage ;;
esac
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java
index 3cfc67824ed..7a314ff0614 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java
@@ -11,7 +11,7 @@ import com.yahoo.vespa.hosted.node.admin.docker.DockerOperationsImpl;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
-import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdaterImpl;
+import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl;
import com.yahoo.vespa.hosted.node.admin.util.Environment;
@@ -40,7 +40,7 @@ public class DockerTester implements AutoCloseable {
final CallOrderVerifier callOrderVerifier = new CallOrderVerifier();
final Docker dockerMock = new DockerMock(callOrderVerifier);
final NodeRepoMock nodeRepositoryMock = new NodeRepoMock(callOrderVerifier);
- final NodeAdminStateUpdaterImpl nodeAdminStateUpdater;
+ final NodeAdminStateUpdater nodeAdminStateUpdater;
final NodeAdmin nodeAdmin;
private final OrchestratorMock orchestratorMock = new OrchestratorMock(callOrderVerifier);
@@ -66,7 +66,7 @@ public class DockerTester implements AutoCloseable {
Function<String, NodeAgent> nodeAgentFactory = (hostName) -> new NodeAgentImpl(hostName, nodeRepositoryMock,
orchestratorMock, dockerOperations, storageMaintainer, aclMaintainer, environment, clock, NODE_AGENT_SCAN_INTERVAL);
nodeAdmin = new NodeAdminImpl(dockerOperations, nodeAgentFactory, storageMaintainer, aclMaintainer, mr, Clock.systemUTC());
- nodeAdminStateUpdater = new NodeAdminStateUpdaterImpl(nodeRepositoryMock, orchestratorMock, storageMaintainer,
+ nodeAdminStateUpdater = new NodeAdminStateUpdater(nodeRepositoryMock, orchestratorMock, storageMaintainer,
nodeAdmin, "basehostname", clock, NODE_ADMIN_CONVERGE_STATE_INTERVAL, new ClassLocking());
nodeAdminStateUpdater.start();
}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java
index 2db93ba4203..0b5d67dd13c 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java
@@ -5,8 +5,7 @@ import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
import com.yahoo.vespa.hosted.node.admin.docker.DockerOperationsImpl;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin;
-import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater;
-import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdaterImpl;
+import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.provision.Node;
import org.junit.Ignore;
import org.junit.Test;
@@ -42,7 +41,7 @@ public class RebootTest {
"createContainerCommand with DockerImage { imageId=dockerImage }, HostName: host1.test.yahoo.com, ContainerName { name=host1 }",
"updateNodeAttributes with HostName: host1.test.yahoo.com, NodeAttributes{restartGeneration=1, rebootGeneration=null, dockerImage=dockerImage, vespaVersion='null'}");
- NodeAdminStateUpdaterImpl updater = dockerTester.nodeAdminStateUpdater;
+ NodeAdminStateUpdater updater = dockerTester.nodeAdminStateUpdater;
assertThat(updater.setResumeStateAndCheckIfResumed(NodeAdminStateUpdater.State.SUSPENDED),
is(Optional.of("Not all node agents are frozen.")));
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java
index a0e122d99fc..23d55bd947c 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java
@@ -15,8 +15,7 @@ import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
-import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater;
-import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdaterImpl;
+import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
@@ -78,7 +77,7 @@ public class RunInContainerTest {
@Before
public void startContainer() throws Exception {
- // To test the initial NodeAdminStateUpdaterImpl convergence towards RESUME, orchestrator should
+ // To test the initial NodeAdminStateUpdater convergence towards RESUME, orchestrator should
// deny permission to resume for parent host, otherwise it'll converge to RESUME before REST
// handler comes up
doThrow(new RuntimeException()).when(orchestratorMock).resume(parentHostname);
@@ -242,7 +241,7 @@ public class RunInContainerTest {
(hostName) -> new NodeAgentImpl(hostName, nodeRepositoryMock, orchestratorMock, dockerOperationsMock,
storageMaintainer, aclMaintainer, environment, Clock.systemUTC(), NODE_AGENT_SCAN_INTERVAL);
private final NodeAdmin nodeAdmin = new NodeAdminImpl(dockerOperationsMock, nodeAgentFactory, storageMaintainer, aclMaintainer, mr, Clock.systemUTC());
- private final NodeAdminStateUpdaterImpl nodeAdminStateUpdater = new NodeAdminStateUpdaterImpl(nodeRepositoryMock,
+ private final NodeAdminStateUpdater nodeAdminStateUpdater = new NodeAdminStateUpdater(nodeRepositoryMock,
orchestratorMock, storageMaintainer, nodeAdmin, "localhost.test.yahoo.com", Clock.systemUTC(), NODE_ADMIN_CONVERGE_STATE_INTERVAL, new ClassLocking());
public NodeAdminProviderWithMocks() {
@@ -250,7 +249,7 @@ public class RunInContainerTest {
}
@Override
- public NodeAdminStateUpdaterImpl get() {
+ public NodeAdminStateUpdater get() {
return nodeAdminStateUpdater;
}
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..6961efc159c
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/io/FileSystemTest.java
@@ -0,0 +1,81 @@
+// 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 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 {
+ String expectedPermissions = "rwxr-x---";
+ fileSystem.setPermissions(path, expectedPermissions);
+ assertEquals(expectedPermissions, fileSystem.getPermissions(path));
+ }
+
+ @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/nodeadmin/NodeAdminStateUpdaterImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java
index 7920d0cad29..1058278a02c 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java
@@ -7,7 +7,6 @@ import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.orchestrator.Orchestrator;
import com.yahoo.vespa.hosted.node.admin.orchestrator.OrchestratorException;
-import com.yahoo.vespa.hosted.node.admin.provider.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.provision.Node;
import org.junit.Test;
@@ -33,11 +32,11 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
/**
- * Basic test of NodeAdminStateUpdaterImpl
+ * Basic test of NodeAdminStateUpdater
*
* @author freva
*/
-public class NodeAdminStateUpdaterImplTest {
+public class NodeAdminStateUpdaterTest {
private final NodeRepository nodeRepository = mock(NodeRepository.class);
private final Orchestrator orchestrator = mock(Orchestrator.class);
private final StorageMaintainer storageMaintainer = mock(StorageMaintainer.class);
@@ -46,7 +45,7 @@ public class NodeAdminStateUpdaterImplTest {
private final ManualClock clock = new ManualClock();
private final Duration convergeStateInterval = Duration.ofSeconds(30);
- private final NodeAdminStateUpdaterImpl refresher = spy(new NodeAdminStateUpdaterImpl(
+ private final NodeAdminStateUpdater refresher = spy(new NodeAdminStateUpdater(
nodeRepository, orchestrator, storageMaintainer, nodeAdmin, parentHostname, clock, convergeStateInterval, null));
@@ -103,7 +102,7 @@ public class NodeAdminStateUpdaterImplTest {
// The second orchestration failure happens after the freeze convergence timeout,
// and so SHOULD call setFrozen(false)
when(nodeAdmin.setFrozen(eq(true))).thenReturn(true);
- when(nodeAdmin.subsystemFreezeDuration()).thenReturn(NodeAdminStateUpdaterImpl.FREEZE_CONVERGENCE_TIMEOUT.plusMinutes(1));
+ when(nodeAdmin.subsystemFreezeDuration()).thenReturn(NodeAdminStateUpdater.FREEZE_CONVERGENCE_TIMEOUT.plusMinutes(1));
doThrow(new RuntimeException("Cannot allow to suspend because some reason")).doNothing()
.when(orchestrator).suspend(eq(parentHostname));
tickAfter(35);
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..9c998cc6fdb
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/WriteFileTaskTest.java
@@ -0,0 +1,80 @@
+// 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.assertEquals;
+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 parentDirectory = Paths.get("/foo");
+ Path path = parentDirectory.resolve("bar");
+
+ @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(contentProducer, times(1)).call();
+ 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<MakeDirectoryTask> makeDirectoryTaskCaptor =
+ ArgumentCaptor.forClass(MakeDirectoryTask.class);
+ verify(contextMock, times(1))
+ .executeSubtask(makeDirectoryTaskCaptor.capture());
+
+ MakeDirectoryTask makeDirectoryTask = makeDirectoryTaskCaptor.getValue();
+ assertEquals(parentDirectory, makeDirectoryTask.getPath());
+ assertTrue(makeDirectoryTask.getWithParents());
+ }
+
+ @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
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileWriterTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileWriterTest.java
deleted file mode 100644
index ca4eabf855b..00000000000
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileWriterTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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.util.file;
-
-import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
-import org.junit.Test;
-
-import java.nio.file.FileSystem;
-import java.nio.file.Path;
-import java.time.Instant;
-
-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.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-public class FileWriterTest {
- private final FileSystem fileSystem = TestFileSystem.create();
-
- @Test
- public void testWrite() {
- final String content = "content";
- final String permissions = "rwxr-xr-x";
- final String owner = "owner";
- final String group = "group";
-
- Path path = fileSystem.getPath("/opt/vespa/tmp/file.txt");
- FileWriter writer = new FileWriter(path, () -> content)
- .withPermissions(permissions)
- .withOwner(owner)
- .withGroup(group);
- TaskContext context = mock(TaskContext.class);
- assertTrue(writer.converge(context));
- verify(context, times(1)).logSystemModification(any(), eq("Writing file " + path));
-
- UnixPath unixPath = new UnixPath(path);
- assertEquals(content, unixPath.readUtf8File());
- assertEquals(permissions, unixPath.getPermissions());
- assertEquals(owner, unixPath.getOwner());
- assertEquals(group, unixPath.getGroup());
- Instant fileTime = unixPath.getLastModifiedTime();
-
- // Second time is a no-op.
- assertFalse(writer.converge(context));
- assertEquals(fileTime, unixPath.getLastModifiedTime());
- }
-}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/TestFileSystem.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/TestFileSystem.java
deleted file mode 100644
index 465cb671a97..00000000000
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/TestFileSystem.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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.util.file;
-
-import com.google.common.jimfs.Configuration;
-import com.google.common.jimfs.Feature;
-import com.google.common.jimfs.Jimfs;
-import com.google.common.jimfs.PathType;
-
-import java.nio.file.FileSystem;
-
-public class TestFileSystem {
- public static FileSystem create() {
- // This configuration is based on Configuration.unix(), except:
- // - Use "posix" attribute view which is necessary for permissions, owner, and group.
- Configuration configuration = Configuration.builder(PathType.unix())
- .setRoots("/")
- .setWorkingDirectory("/work")
- .setAttributeViews("posix")
- .setSupportedFeatures(Feature.LINKS, Feature.SYMBOLIC_LINKS, Feature.SECURE_DIRECTORY_STREAM, Feature.FILE_CHANNEL)
- .build();
- return Jimfs.newFileSystem(configuration);
- }
-}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPathTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPathTest.java
deleted file mode 100644
index 821c6397ee7..00000000000
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPathTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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.util.file;
-
-import org.junit.Test;
-
-import java.nio.file.FileSystem;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class UnixPathTest {
- final FileSystem fileSystem = TestFileSystem.create();
-
- @Test
- public void createParents() throws Exception {
- Path parentDirectory = fileSystem.getPath("/a/b/c");
- Path filePath = parentDirectory.resolve("bar");
- UnixPath path = new UnixPath(filePath);
-
- assertFalse(Files.exists(fileSystem.getPath("/a")));
- path.createParents();
- assertTrue(Files.exists(parentDirectory));
- }
-
- @Test
- public void utf8File() throws Exception {
- String original = "foo\nbar\n";
- UnixPath path = new UnixPath(fileSystem.getPath("example.txt"));
- path.writeUtf8File(original);
- String fromFile = path.readUtf8File();
- assertEquals(original, fromFile);
- }
-
- @Test
- public void permissions() throws Exception {
- String expectedPermissions = "rwxr-x---";
- UnixPath path = new UnixPath(fileSystem.getPath("file.txt"));
- path.writeUtf8File("foo");
- path.setPermissions(expectedPermissions);
- assertEquals(expectedPermissions, path.getPermissions());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void badPermissionsString() {
- new UnixPath(fileSystem.getPath("file.txt")).setPermissions("abcdefghi");
- }
-
- @Test
- public void owner() throws Exception {
- FileSystem fs = TestFileSystem.create();
- Path path = fs.getPath("file.txt");
- UnixPath unixPath = new UnixPath(path);
- unixPath.writeUtf8File("foo");
-
- unixPath.setOwner("owner");
- assertEquals("owner", unixPath.getOwner());
-
- unixPath.setGroup("group");
- assertEquals("group", unixPath.getGroup());
- }
-}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/AddYumRepoTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/AddYumRepoTest.java
deleted file mode 100644
index 7b6ab91345b..00000000000
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/AddYumRepoTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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.util.yum;
-
-import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
-import com.yahoo.vespa.hosted.node.admin.task.util.file.TestFileSystem;
-import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;
-import org.junit.Test;
-
-import java.nio.file.FileSystem;
-import java.time.Instant;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class AddYumRepoTest {
- @Test
- public void converge() throws Exception {
- String repositoryId = "repoid";
- String name = "name";
- String baseurl = "http://foo.com/bar";
- boolean enabled = true;
-
- AddYumRepo addYumRepo = new AddYumRepo(
- repositoryId,
- name,
- baseurl,
- enabled);
-
- TaskContext context = mock(TaskContext.class);
-
- FileSystem fileSystem = TestFileSystem.create();
- when(context.fileSystem()).thenReturn(fileSystem);
-
- assertTrue(addYumRepo.converge(context));
-
- UnixPath unixPath = new UnixPath(fileSystem.getPath("/etc/yum.repos.d/" + repositoryId + ".repo"));
- String content = unixPath.readUtf8File();
- assertEquals("# This file was generated by node admin\n" +
- "# Do NOT modify this file by hand\n" +
- "\n" +
- "[repoid]\n" +
- "name=name\n" +
- "baseurl=http://foo.com/bar\n" +
- "enabled=1\n" +
- "gpgcheck=0\n", content);
- Instant lastModifiedTime = unixPath.getLastModifiedTime();
-
- // Second time is a no-op
- assertFalse(addYumRepo.converge(context));
- assertEquals(lastModifiedTime, unixPath.getLastModifiedTime());
- }
-
-} \ No newline at end of file