diff options
author | Håkon Hallingstad <hakon@oath.com> | 2018-01-30 00:16:20 +0100 |
---|---|---|
committer | Håkon Hallingstad <hakon@oath.com> | 2018-01-30 00:16:20 +0100 |
commit | dd6de3c18ed9f5a6d89f843b8a0835c8e8480a9d (patch) | |
tree | ba1010d94236d797fc2b27f8072674eedce9c705 /node-admin/src/main/java | |
parent | 0e6ec009fcce1796b4d9ad0ad7accaa403221930 (diff) |
Implement directory resource
Diffstat (limited to 'node-admin/src/main/java')
5 files changed, 225 insertions, 57 deletions
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 index 87491367514..cee2dc9b66b 100644 --- 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 @@ -18,6 +18,9 @@ public interface TaskContext { FileSystem fileSystem(); void logSystemModification(Logger logger, String actionDescription); + default void logSystemModification(Logger logger, String format, String... args) { + logSystemModification(logger, String.format(format, (Object[]) args)); + } default boolean executeSubtask(IdempotentTask task) { return false; } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/AttributeSync.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/AttributeSync.java new file mode 100644 index 00000000000..a20d30b2bf9 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/AttributeSync.java @@ -0,0 +1,126 @@ +// 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 java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Logger; + +/** + * Class to converge file/directory attributes like owner and permissions to wanted values. + * Typically used by higher abstraction layers working on files (FileSync/FileWriter) or + * directories (MakeDirectory). + * + * @author hakonhall + */ +public class AttributeSync { + private static final Logger logger = Logger.getLogger(AttributeSync.class.getName()); + + private final UnixPath path; + + private Optional<String> owner = Optional.empty(); + private Optional<String> group = Optional.empty(); + private Optional<String> permissions = Optional.empty(); + + public AttributeSync(Path path) { + this.path = new UnixPath(path); + } + + public Optional<String> getPermissions() { + return permissions; + } + + public AttributeSync withPermissions(String permissions) { + this.permissions = Optional.of(permissions); + return this; + } + + public Optional<String> getOwner() { + return owner; + } + + public AttributeSync withOwner(String owner) { + this.owner = Optional.of(owner); + return this; + } + + public Optional<String> getGroup() { + return group; + } + + public AttributeSync withGroup(String group) { + this.group = Optional.of(group); + return this; + } + + public AttributeSync with(PartialFileData fileData) { + owner = fileData.getOwner(); + group = fileData.getGroup(); + permissions = fileData.getPermissions(); + return this; + } + + public boolean converge(TaskContext context) { + return converge(context, new FileAttributesCache(path)); + } + + /** + * Path must exist before calling converge. + */ + public boolean converge(TaskContext context, FileAttributesCache currentAttributes) { + boolean systemModified = updateAttribute( + context, + "owner", + owner, + () -> currentAttributes.get().owner(), + path::setOwner); + + systemModified |= updateAttribute( + context, + "group", + group, + () -> currentAttributes.get().group(), + path::setGroup); + + systemModified |= updateAttribute( + context, + "permissions", + permissions, + () -> currentAttributes.get().permissions(), + path::setPermissions); + + return systemModified; + } + + private boolean updateAttribute(TaskContext context, + String attributeName, + Optional<String> wantedValue, + Supplier<String> currentValueSupplier, + Consumer<String> valueSetter) { + if (!wantedValue.isPresent()) { + return false; + } + + String currentValue = currentValueSupplier.get(); + if (Objects.equals(currentValue, wantedValue.get())) { + return false; + } + + context.logSystemModification( + logger, + "Changing %s of %s from %s to %s", + attributeName, + path.toString(), + currentValue, + wantedValue.get()); + + valueSetter.accept(wantedValue.get()); + + return true; + } +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileSync.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileSync.java index d8b8aadfff7..18d2a2e3aa9 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileSync.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileSync.java @@ -7,8 +7,6 @@ import com.yahoo.vespa.hosted.node.admin.component.TaskContext; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Supplier; import java.util.logging.Logger; /** @@ -39,58 +37,14 @@ public class FileSync { public boolean convergeTo(TaskContext taskContext, PartialFileData partialFileData) { FileAttributesCache currentAttributes = new FileAttributesCache(path); - boolean modifiedSystem = false; + boolean modifiedSystem = maybeUpdateContent(taskContext, partialFileData.getContent(), currentAttributes); - modifiedSystem |= maybeUpdateContent(taskContext, partialFileData.getContent(), currentAttributes); - - modifiedSystem |= convergeAttribute( - taskContext, - "owner", - partialFileData.getOwner(), - () -> currentAttributes.get().owner(), - path::setOwner); - - modifiedSystem |= convergeAttribute( - taskContext, - "group", - partialFileData.getGroup(), - () -> currentAttributes.get().group(), - path::setGroup); - - modifiedSystem |= convergeAttribute( - taskContext, - "permissions", - partialFileData.getPermissions(), - () -> currentAttributes.get().permissions(), - path::setPermissions); + AttributeSync attributeSync = new AttributeSync(path.toPath()).with(partialFileData); + modifiedSystem |= attributeSync.converge(taskContext, currentAttributes); return modifiedSystem; } - private boolean convergeAttribute(TaskContext taskContext, - String attributeName, - Optional<String> wantedValue, - Supplier<String> currentValueSupplier, - Consumer<String> valueSetter) { - if (!wantedValue.isPresent()) { - return false; - } - - String currentValue = currentValueSupplier.get(); - if (Objects.equals(wantedValue.get(), currentValue)) { - return false; - } else { - String actionDescription = String.format("Changing %s of %s from %s to %s", - attributeName, - path, - currentValue, - wantedValue.get()); - taskContext.logSystemModification(logger, actionDescription); - valueSetter.accept(wantedValue.get()); - return true; - } - } - private boolean maybeUpdateContent(TaskContext taskContext, Optional<String> content, FileAttributesCache currentAttributes) { diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/MakeDirectory.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/MakeDirectory.java new file mode 100644 index 00000000000..e815ab8bd86 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/MakeDirectory.java @@ -0,0 +1,72 @@ +// 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 java.io.UncheckedIOException; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +/** + * Class to ensure a directory exists with the correct owner, group, and permissions. + * + * @author hakonhall + */ +public class MakeDirectory { + private static final Logger logger = Logger.getLogger(MakeDirectory.class.getName()); + + private final UnixPath path; + private final AttributeSync attributeSync; + + private boolean createParents = false; + + public MakeDirectory(Path path) { + this.path = new UnixPath(path); + this.attributeSync = new AttributeSync(path); + } + + /** + * Warning: The owner, group, and permissions of any created parent directories are NOT modified + */ + public MakeDirectory createParents() { this.createParents = true; return this; } + + public MakeDirectory withOwner(String owner) { attributeSync.withOwner(owner); return this; } + public MakeDirectory withGroup(String group) { attributeSync.withGroup(group); return this; } + public MakeDirectory withPermissions(String permissions) { + attributeSync.withPermissions(permissions); + return this; + } + + public boolean converge(TaskContext context) { + boolean systemModified = false; + + FileAttributesCache attributes = new FileAttributesCache(path); + if (attributes.exists()) { + if (!attributes.get().isDirectory()) { + throw new UncheckedIOException(new NotDirectoryException(path.toString())); + } + } else { + if (createParents) { + // We'll skip logginer system modification here, as we'll log about the creation + // of the directory next. + path.createParents(); + } + + context.logSystemModification(logger, "Creating directory " + path); + systemModified = true; + + Optional<String> permissions = attributeSync.getPermissions(); + if (permissions.isPresent()) { + path.createDirectory(permissions.get()); + } else { + path.createDirectory(); + } + } + + systemModified |= attributeSync.converge(context, attributes); + + return systemModified; + } +} 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/task/util/file/UnixPath.java index aaffea05d1e..ac4230ca7c6 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/task/util/file/UnixPath.java @@ -6,6 +6,7 @@ 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; @@ -69,14 +70,7 @@ public class UnixPath { * and no permissions for others. */ public void setPermissions(String permissions) { - Set<PosixFilePermission> permissionSet; - try { - permissionSet = PosixFilePermissions.fromString(permissions); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Failed to set permissions '" + - permissions + "' on path " + path, e); - } - + Set<PosixFilePermission> permissionSet = getPosixFilePermissionsFromString(permissions); uncheck(() -> Files.setPosixFilePermissions(path, permissionSet)); } @@ -114,8 +108,27 @@ public class UnixPath { return IOExceptionUtil.ifExists(() -> getAttributes()); } + public void createDirectory(String permissions) { + Set<PosixFilePermission> set = getPosixFilePermissionsFromString(permissions); + FileAttribute<Set<PosixFilePermission>> attribute = PosixFilePermissions.asFileAttribute(set); + uncheck(() -> Files.createDirectory(path, attribute)); + } + + public void createDirectory() { + uncheck(() -> Files.createDirectory(path)); + } + @Override public String toString() { return path.toString(); } + + private Set<PosixFilePermission> getPosixFilePermissionsFromString(String permissions) { + try { + return PosixFilePermissions.fromString(permissions); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Failed to set permissions '" + + permissions + "' on path " + path, e); + } + } } |