diff options
39 files changed, 655 insertions, 540 deletions
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java b/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java index f0dc26844e7..c94ebc1ab93 100644 --- a/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java +++ b/jdisc_core/src/main/java/com/yahoo/jdisc/Timer.java @@ -4,8 +4,6 @@ package com.yahoo.jdisc; import com.google.inject.ImplementedBy; import com.yahoo.jdisc.core.SystemTimer; -import java.time.Instant; - /** * <p>This class provides access to the current time in milliseconds, as viewed by the {@link Container}. Inject an * instance of this class into any component that needs to access time, instead of using @@ -29,10 +27,4 @@ public interface Timer { */ long currentTimeMillis(); - /** - * Convenience method for getting an java.util.Instance from currentTimeMillis(). - */ - default Instant currentTime() { - return Instant.ofEpochMilli(currentTimeMillis()); - } } 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 diff --git a/parent/pom.xml b/parent/pom.xml index 46b8c90baef..de02f5085ea 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -607,12 +607,6 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.google.jimfs</groupId> - <artifactId>jimfs</artifactId> - <version>1.1</version> - <scope>test</scope> - </dependency> - <dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.compendium</artifactId> <version>4.3.0</version> diff --git a/standalone-container/src/main/sh/standalone-container.sh b/standalone-container/src/main/sh/standalone-container.sh index 02c71beaac5..dd0693f6f85 100755 --- a/standalone-container/src/main/sh/standalone-container.sh +++ b/standalone-container/src/main/sh/standalone-container.sh @@ -67,7 +67,6 @@ Manage Vespa standalone jdisc container service. Options: -u USER Run as USER. Overrides any VESPA_USER environment variable. -s SERVICE The service name. - -- ARGS... Pass the rest of the arguments (ARGS) to the Java invocation EOF exit 1 @@ -90,19 +89,16 @@ FixDataDirectory() { StartCommand() { local service="$1" shift - local -a jvm_arguments=("$@") + + if (( $# > 0 )); then + Fail "Too many arguments" + fi local service_regex='^[0-9a-zA-Z_-]+$' if ! [[ "$service" =~ $service_regex ]]; then Fail "Service must match regex '$service_regex'" fi - local pidfile="$VESPA_HOME/var/run/$service.pid" - if test -r "$pidfile"; then - echo "$service is already running as PID $(< "$pidfile") according to $pidfile" - return - fi - # common setup export VESPA_SERVICE_NAME="$service" @@ -139,7 +135,6 @@ StartCommand() { FixDataDirectory "$bundlecachedir" java \ - "${jvm_arguments[@]}" \ -Xms128m -Xmx2048m \ -XX:+PreserveFramePointer \ -XX:+HeapDumpOnOutOfMemoryError \ @@ -272,7 +267,6 @@ Main() { local service="standalone/container" local user="$VESPA_USER" - local -a jvm_arguments=() while (( $# > 0 )); do case "$1" in @@ -285,11 +279,6 @@ Main() { user="$2" shift 2 ;; - --) - shift - jvm_arguments=("$@") - break - ;; *) break ;; esac done @@ -311,7 +300,7 @@ Main() { case "$command" in help) Usage ;; - start) StartCommand "$service" "${jvm_arguments[@]}" ;; + start) StartCommand "$service" "$@" ;; stop) StopCommand "$user" "$service" "$@" ;; *) Fail "Unknown command '$command'" ;; esac |