Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.apache.ignite.internal.deployunit.NodesToDeploy;
import org.apache.ignite.internal.deployunit.UnitStatuses;
import org.apache.ignite.internal.deployunit.exception.DeploymentUnitNotFoundException;
import org.apache.ignite.internal.deployunit.structure.UnitFolder;
import org.apache.ignite.internal.manager.ComponentContext;

/**
Expand Down Expand Up @@ -118,6 +119,11 @@ public CompletableFuture<Version> detectLatestDeployedVersion(String id) {
.orElseThrow());
}

@Override
public CompletableFuture<UnitFolder> nodeUnitFileStructure(String unitId, Version version) {
throw new UnsupportedOperationException("Not implemented");
}

@Override
public CompletableFuture<Void> startAsync(ComponentContext componentContext) {
return nullCompletedFuture();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,11 @@ public Unit deployAndVerify(

assertThat(deploy, willBe(true));

Unit unit = new Unit(entryNode, workDir, id, version, files);
Unit unit = new Unit(entryNode, id, version, files);

for (DeployFile file : files) {
unit.verify(file, entryNode);
unit.verifyByRest(entryNode);
}

return unit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
package org.apache.ignite.internal.deployment;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

Expand All @@ -33,7 +35,9 @@
import org.apache.ignite.deployment.version.Version;
import org.apache.ignite.internal.app.IgniteImpl;
import org.apache.ignite.internal.deployunit.configuration.DeploymentExtensionConfiguration;
import org.hamcrest.Matchers;
import org.apache.ignite.internal.deployunit.structure.UnitEntry;
import org.apache.ignite.internal.deployunit.structure.UnitFile;
import org.apache.ignite.internal.deployunit.structure.UnitFolder;

class Unit {
private final IgniteImpl deployedNode;
Expand All @@ -44,11 +48,8 @@ class Unit {

private final List<DeployFile> files;

private final Path workDir;

Unit(IgniteImpl deployedNode, Path workDir, String id, Version version, List<DeployFile> files) {
Unit(IgniteImpl deployedNode, String id, Version version, List<DeployFile> files) {
this.deployedNode = deployedNode;
this.workDir = workDir;
this.id = id;
this.version = version;
this.files = files;
Expand Down Expand Up @@ -143,10 +144,42 @@ public void verify(DeployFile file, IgniteImpl entryNode) {
try {
Path filePath = nodeUnitDirectory.resolve(file.file().getFileName());
assertTrue(Files.exists(filePath));
assertThat(Files.size(filePath), Matchers.is(file.expectedSize()));
assertThat(Files.size(filePath), is(file.expectedSize()));
} catch (IOException e) {
fail(e);
}
}
}

void verifyByRest(IgniteImpl entryNode) {
Path currentDir = getNodeUnitDirectory(entryNode);

CompletableFuture<Void> result = entryNode.deployment().nodeUnitFileStructure(id, version).thenAccept(folder -> {
try {
for (UnitEntry child : folder.children()) {
processEntry(child, currentDir);
}
} catch (IOException e) {
fail(e);
}
});

assertThat(result, willCompleteSuccessfully());
}

private static void processEntry(UnitEntry entry, Path currentDir) throws IOException {
if (entry instanceof UnitFile) {
UnitFile file = (UnitFile) entry;
Path filePath = currentDir.resolve(file.name());
assertTrue(Files.exists(filePath));
assertThat(Files.size(filePath), is(file.size()));
} else if (entry instanceof UnitFolder) {
Path dir = currentDir.resolve(entry.name());
for (UnitEntry child : ((UnitFolder) entry).children()) {
processEntry(child, dir);
}
} else {
fail(new IllegalStateException("Unit entry type not supported."));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.apache.ignite.internal.deployunit.metastore.NodeStatusWatchListener;
import org.apache.ignite.internal.deployunit.metastore.status.UnitClusterStatus;
import org.apache.ignite.internal.deployunit.metastore.status.UnitNodeStatus;
import org.apache.ignite.internal.deployunit.structure.UnitFolder;
import org.apache.ignite.internal.deployunit.tempstorage.TempStorageProvider;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
Expand Down Expand Up @@ -354,6 +355,20 @@ public CompletableFuture<DeploymentStatus> nodeStatusAsync(String id, Version ve
.thenApply(DeploymentManagerImpl::extractDeploymentStatus);
}

@Override
public CompletableFuture<UnitFolder> nodeUnitFileStructure(String id, Version version) {
checkId(id);
Objects.requireNonNull(version);

return deploymentUnitStore.getNodeStatus(nodeName, id, version).thenCompose(nodeStatus -> {
if (nodeStatus == null) {
return failedFuture(new DeploymentUnitNotFoundException(id, version));
}

return deployer.getUnitStructure(id, version);
});
}

@Override
public CompletableFuture<Boolean> onDemandDeploy(String id, Version version) {
return deploymentUnitStore.getAllNodeStatuses(id, version)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@
import org.apache.ignite.deployment.version.Version;
import org.apache.ignite.internal.deployunit.DeployerProcessor.DeployArg;
import org.apache.ignite.internal.deployunit.exception.DeploymentUnitNotFoundException;
import org.apache.ignite.internal.deployunit.structure.UnitFolder;
import org.apache.ignite.internal.deployunit.tempstorage.TempStorage;
import org.apache.ignite.internal.deployunit.tempstorage.TempStorageProvider;
import org.apache.ignite.internal.deployunit.tempstorage.TempStorageProviderImpl;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.thread.IgniteThreadFactory;
import org.apache.ignite.internal.util.IgniteUtils;
import org.jetbrains.annotations.Nullable;

/**
* Service for file deploying on local File System.
Expand Down Expand Up @@ -144,6 +146,51 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
}, executor);
}

/**
* Returns unit content representation with tree structure.
*
* @param id Unit identifier.
* @param version Unit version.
*/
public CompletableFuture<UnitFolder> getUnitStructure(String id, Version version) {
return CompletableFuture.supplyAsync(() -> {
try {
UnitStructureBuilder builder = new UnitStructureBuilder();
Path unitFolder = unitPath(id, version);
builder.pushFolder(id + "-" + version);

Files.walkFileTree(unitFolder, new SimpleFileVisitor<>() {

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
if (!Files.isSameFile(dir, unitFolder)) {
builder.pushFolder(dir.getFileName().toString());
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
builder.addFile(file.getFileName().toString(), attrs.size());
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path dir, @Nullable IOException exc) throws IOException {
if (!Files.isSameFile(dir, unitFolder)) {
builder.popFolder(dir.getFileName().toString());
}
return FileVisitResult.CONTINUE;
}
});
return builder.build();
} catch (IOException e) {
LOG.error("Failed to get content for unit " + id + ":" + version, e);
return null;
}
}, executor);
}

/**
* Returns path to unit folder.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.ignite.deployment.version.Version;
import org.apache.ignite.internal.deployunit.structure.UnitFolder;
import org.apache.ignite.internal.manager.IgniteComponent;

/**
Expand Down Expand Up @@ -149,4 +150,12 @@ CompletableFuture<Boolean> deployAsync(
* @return Future with the latest version of the deployment unit.
*/
CompletableFuture<Version> detectLatestDeployedVersion(String id);

/**
* Returns unit content tree structure.
*
* @param unitId Deployment unit identifier.
* @param version Deployment unit version.
*/
CompletableFuture<UnitFolder> nodeUnitFileStructure(String unitId, Version version);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.ignite.internal.deployunit;

import java.util.ArrayDeque;
import java.util.Deque;
import org.apache.ignite.internal.deployunit.structure.UnitFile;
import org.apache.ignite.internal.deployunit.structure.UnitFolder;

/**
* Builder for constructing hierarchical deployment unit structures.
*
* <p>This class uses a stack-based approach to build a tree structure of folders and files
* representing the content of a deployment unit. Folders can be nested by pushing and popping
* them on the internal stack, while files are added to the current (top) folder.
*
* @see UnitFolder
* @see UnitFile
*/
public class UnitStructureBuilder {
/** Stack of folders used to build the hierarchical structure. */
private final Deque<UnitFolder> folderStack = new ArrayDeque<>();

/**
* Pushes a new folder onto the stack and adds it as a child to the current folder.
*
* <p>If there is already a folder on the stack, the new folder is added as a child
* to it. The new folder becomes the current folder (top of the stack) to which
* subsequent files and folders will be added.
*
* @param folderName the name of the folder to push
*/
public void pushFolder(String folderName) {
UnitFolder folder = folderStack.peek();
UnitFolder newFolder = new UnitFolder(folderName);
if (folder != null) {
folder.addChild(newFolder);
}
folderStack.push(newFolder);
}

/**
* Pops the current folder from the stack.
*
* <p>The provided folder name is verified against the name of the folder being popped
* to ensure correct nesting structure.
*
* @param folderName the expected name of the folder being popped (for verification)
*/
public void popFolder(String folderName) {
UnitFolder lastFolder = folderStack.pop();
assert lastFolder.name().equals(folderName);
}

/**
* Adds a file to the current folder.
*
* <p>The file is added as a child to the folder at the top of the stack.
*
* @param fileName the name of the file
* @param size the size of the file in bytes
*/
public void addFile(String fileName, long size) {
UnitFolder folder = folderStack.peek();
folder.addChild(new UnitFile(fileName, size));
}

/**
* Builds and returns the root folder structure.
*
* <p>This method pops and returns the last remaining folder from the stack,
* which should be the root of the built structure. The stack should contain
* exactly one folder when this method is called.
*
* @return the root folder of the built structure
* @throws java.util.NoSuchElementException if the stack is empty
*/
public UnitFolder build() {
return folderStack.pop();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.ignite.internal.deployunit.structure;

/**
* Represents an entry in a deployment unit's content structure.
*
* <p>A unit entry can be either a file or a folder within a deployment unit.
* This interface provides basic operations to access the entry's metadata such as name and size.
*
* @see UnitFile
* @see UnitFolder
*/
public interface UnitEntry {
/**
* Returns the name of this entry.
*
* <p>For files, this is the file name. For folders, this is the folder name.
*
* @return the entry name, never {@code null}
*/
String name();

/**
* Returns the size of this entry in bytes.
*
* <p>For files, this is the actual file size. For folders, this is the sum
* of all children sizes (recursive).
*
* @return the entry size in bytes, always non-negative
*/
long size();
}
Loading