Skip to content

Commit 84ef95f

Browse files
kelywelsch
authored andcommitted
Do not create directories if repository is readonly (#26909)
For FsBlobStore and HdfsBlobStore, if the repository is read only, the blob store should be aware of the readonly setting and do not create directories if they don't exist. Closes #21495
1 parent 154d388 commit 84ef95f

File tree

7 files changed

+118
-27
lines changed

7 files changed

+118
-27
lines changed

core/src/main/java/org/elasticsearch/common/blobstore/fs/FsBlobStore.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ public class FsBlobStore extends AbstractComponent implements BlobStore {
3939

4040
private final int bufferSizeInBytes;
4141

42+
private final boolean readOnly;
43+
4244
public FsBlobStore(Settings settings, Path path) throws IOException {
4345
super(settings);
4446
this.path = path;
45-
Files.createDirectories(path);
47+
this.readOnly = settings.getAsBoolean("readonly", false);
48+
if (!this.readOnly) {
49+
Files.createDirectories(path);
50+
}
4651
this.bufferSizeInBytes = (int) settings.getAsBytesSize("repositories.fs.buffer_size", new ByteSizeValue(100, ByteSizeUnit.KB)).getBytes();
4752
}
4853

@@ -80,7 +85,9 @@ public void close() {
8085

8186
private synchronized Path buildAndCreate(BlobPath path) throws IOException {
8287
Path f = buildPath(path);
83-
Files.createDirectories(f);
88+
if (!readOnly) {
89+
Files.createDirectories(f);
90+
}
8491
return f;
8592
}
8693

core/src/test/java/org/elasticsearch/common/blobstore/FsBlobStoreTests.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020

2121
import org.apache.lucene.util.LuceneTestCase;
2222
import org.elasticsearch.common.blobstore.fs.FsBlobStore;
23+
import org.elasticsearch.common.bytes.BytesArray;
2324
import org.elasticsearch.common.settings.Settings;
2425
import org.elasticsearch.common.unit.ByteSizeUnit;
2526
import org.elasticsearch.common.unit.ByteSizeValue;
2627
import org.elasticsearch.repositories.ESBlobStoreTestCase;
2728

2829
import java.io.IOException;
30+
import java.nio.file.Files;
2931
import java.nio.file.Path;
3032

3133
@LuceneTestCase.SuppressFileSystems("ExtrasFS")
@@ -35,4 +37,39 @@ protected BlobStore newBlobStore() throws IOException {
3537
Settings settings = randomBoolean() ? Settings.EMPTY : Settings.builder().put("buffer_size", new ByteSizeValue(randomIntBetween(1, 100), ByteSizeUnit.KB)).build();
3638
return new FsBlobStore(settings, tempDir);
3739
}
40+
41+
public void testReadOnly() throws Exception {
42+
Settings settings = Settings.builder().put("readonly", true).build();
43+
Path tempDir = createTempDir();
44+
Path path = tempDir.resolve("bar");
45+
46+
try (FsBlobStore store = new FsBlobStore(settings, path)) {
47+
assertFalse(Files.exists(path));
48+
BlobPath blobPath = BlobPath.cleanPath().add("foo");
49+
store.blobContainer(blobPath);
50+
Path storePath = store.path();
51+
for (String d : blobPath) {
52+
storePath = storePath.resolve(d);
53+
}
54+
assertFalse(Files.exists(storePath));
55+
}
56+
57+
settings = randomBoolean() ? Settings.EMPTY : Settings.builder().put("readonly", false).build();
58+
try (FsBlobStore store = new FsBlobStore(settings, path)) {
59+
assertTrue(Files.exists(path));
60+
BlobPath blobPath = BlobPath.cleanPath().add("foo");
61+
BlobContainer container = store.blobContainer(blobPath);
62+
Path storePath = store.path();
63+
for (String d : blobPath) {
64+
storePath = storePath.resolve(d);
65+
}
66+
assertTrue(Files.exists(storePath));
67+
assertTrue(Files.isDirectory(storePath));
68+
69+
byte[] data = randomBytes(randomIntBetween(10, scaledRandomIntBetween(1024, 1 << 16)));
70+
writeBlob(container, "test", new BytesArray(data));
71+
assertArrayEquals(readBlobFully(container, "test", data.length), data);
72+
assertTrue(container.blobExists("test"));
73+
}
74+
}
3875
}

plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsBlobStore.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,21 @@ final class HdfsBlobStore implements BlobStore {
3939
private final FileContext fileContext;
4040
private final HdfsSecurityContext securityContext;
4141
private final int bufferSize;
42+
private final boolean readOnly;
4243
private volatile boolean closed;
4344

44-
HdfsBlobStore(FileContext fileContext, String path, int bufferSize) throws IOException {
45+
HdfsBlobStore(FileContext fileContext, String path, int bufferSize, boolean readOnly) throws IOException {
4546
this.fileContext = fileContext;
4647
this.securityContext = new HdfsSecurityContext(fileContext.getUgi());
4748
this.bufferSize = bufferSize;
4849
this.root = execute(fileContext1 -> fileContext1.makeQualified(new Path(path)));
49-
try {
50-
mkdirs(root);
51-
} catch (FileAlreadyExistsException ok) {
52-
// behaves like Files.createDirectories
50+
this.readOnly = readOnly;
51+
if (!readOnly) {
52+
try {
53+
mkdirs(root);
54+
} catch (FileAlreadyExistsException ok) {
55+
// behaves like Files.createDirectories
56+
}
5357
}
5458
}
5559

@@ -80,12 +84,14 @@ public BlobContainer blobContainer(BlobPath path) {
8084

8185
private Path buildHdfsPath(BlobPath blobPath) {
8286
final Path path = translateToHdfsPath(blobPath);
83-
try {
84-
mkdirs(path);
85-
} catch (FileAlreadyExistsException ok) {
86-
// behaves like Files.createDirectories
87-
} catch (IOException ex) {
88-
throw new ElasticsearchException("failed to create blob container", ex);
87+
if (!readOnly) {
88+
try {
89+
mkdirs(path);
90+
} catch (FileAlreadyExistsException ok) {
91+
// behaves like Files.createDirectories
92+
} catch (IOException ex) {
93+
throw new ElasticsearchException("failed to create blob container", ex);
94+
}
8995
}
9096
return path;
9197
}

plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ protected void doStart() {
106106
SpecialPermission.check();
107107
FileContext fileContext = AccessController.doPrivileged((PrivilegedAction<FileContext>)
108108
() -> createContext(uri, getMetadata().settings()));
109-
blobStore = new HdfsBlobStore(fileContext, pathSetting, bufferSize);
109+
blobStore = new HdfsBlobStore(fileContext, pathSetting, bufferSize, isReadOnly());
110110
logger.debug("Using file-system [{}] for URI [{}], path [{}]", fileContext.getDefaultFileSystem(), fileContext.getDefaultFileSystem().getUri(), pathSetting);
111111
} catch (IOException e) {
112112
throw new UncheckedIOException(String.format(Locale.ROOT, "Cannot create HDFS repository for uri [%s]", uri), e);

plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsBlobStoreContainerTests.java

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@
1919

2020
package org.elasticsearch.repositories.hdfs;
2121

22+
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
23+
import org.apache.hadoop.conf.Configuration;
24+
import org.apache.hadoop.fs.AbstractFileSystem;
25+
import org.apache.hadoop.fs.FileContext;
26+
import org.apache.hadoop.fs.Path;
27+
import org.apache.hadoop.fs.UnsupportedFileSystemException;
28+
import org.elasticsearch.common.SuppressForbidden;
29+
import org.elasticsearch.common.blobstore.BlobContainer;
30+
import org.elasticsearch.common.blobstore.BlobPath;
31+
import org.elasticsearch.common.blobstore.BlobStore;
32+
import org.elasticsearch.common.bytes.BytesArray;
33+
import org.elasticsearch.repositories.ESBlobStoreContainerTestCase;
34+
35+
import javax.security.auth.Subject;
2236
import java.io.IOException;
2337
import java.lang.reflect.Constructor;
2438
import java.lang.reflect.InvocationTargetException;
@@ -29,30 +43,28 @@
2943
import java.security.PrivilegedActionException;
3044
import java.security.PrivilegedExceptionAction;
3145
import java.util.Collections;
32-
import javax.security.auth.Subject;
3346

34-
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
35-
import org.apache.hadoop.conf.Configuration;
36-
import org.apache.hadoop.fs.AbstractFileSystem;
37-
import org.apache.hadoop.fs.FileContext;
38-
import org.apache.hadoop.fs.UnsupportedFileSystemException;
39-
import org.elasticsearch.common.SuppressForbidden;
40-
import org.elasticsearch.common.blobstore.BlobStore;
41-
import org.elasticsearch.repositories.ESBlobStoreContainerTestCase;
47+
import static org.elasticsearch.repositories.ESBlobStoreTestCase.randomBytes;
48+
import static org.elasticsearch.repositories.ESBlobStoreTestCase.readBlobFully;
49+
4250

4351
@ThreadLeakFilters(filters = {HdfsClientThreadLeakFilter.class})
4452
public class HdfsBlobStoreContainerTests extends ESBlobStoreContainerTestCase {
4553

4654
@Override
4755
protected BlobStore newBlobStore() throws IOException {
56+
return new HdfsBlobStore(createTestContext(), "temp", 1024, false);
57+
}
58+
59+
private FileContext createTestContext() {
4860
FileContext fileContext;
4961
try {
5062
fileContext = AccessController.doPrivileged((PrivilegedExceptionAction<FileContext>)
5163
() -> createContext(new URI("hdfs:///")));
5264
} catch (PrivilegedActionException e) {
5365
throw new RuntimeException(e.getCause());
5466
}
55-
return new HdfsBlobStore(fileContext, "temp", 1024);
67+
return fileContext;
5668
}
5769

5870
@SuppressForbidden(reason = "lesser of two evils (the other being a bunch of JNI/classloader nightmares)")
@@ -69,7 +81,7 @@ private FileContext createContext(URI uri) {
6981
Class<?> clazz = Class.forName("org.apache.hadoop.security.User");
7082
ctor = clazz.getConstructor(String.class);
7183
ctor.setAccessible(true);
72-
} catch (ClassNotFoundException | NoSuchMethodException e) {
84+
} catch (ClassNotFoundException | NoSuchMethodException e) {
7385
throw new RuntimeException(e);
7486
}
7587

@@ -98,4 +110,33 @@ private FileContext createContext(URI uri) {
98110
}
99111
});
100112
}
113+
114+
public void testReadOnly() throws Exception {
115+
FileContext fileContext = createTestContext();
116+
// Constructor will not create dir if read only
117+
HdfsBlobStore hdfsBlobStore = new HdfsBlobStore(fileContext, "dir", 1024, true);
118+
FileContext.Util util = fileContext.util();
119+
Path root = fileContext.makeQualified(new Path("dir"));
120+
assertFalse(util.exists(root));
121+
BlobPath blobPath = BlobPath.cleanPath().add("path");
122+
123+
// blobContainer() will not create path if read only
124+
hdfsBlobStore.blobContainer(blobPath);
125+
Path hdfsPath = root;
126+
for (String p : blobPath) {
127+
hdfsPath = new Path(hdfsPath, p);
128+
}
129+
assertFalse(util.exists(hdfsPath));
130+
131+
// if not read only, directory will be created
132+
hdfsBlobStore = new HdfsBlobStore(fileContext, "dir", 1024, false);
133+
assertTrue(util.exists(root));
134+
BlobContainer container = hdfsBlobStore.blobContainer(blobPath);
135+
assertTrue(util.exists(hdfsPath));
136+
137+
byte[] data = randomBytes(randomIntBetween(10, scaledRandomIntBetween(1024, 1 << 16)));
138+
writeBlob(container, "foo", new BytesArray(data));
139+
assertArrayEquals(readBlobFully(container, "foo", data.length), data);
140+
assertTrue(container.blobExists("foo"));
141+
}
101142
}

test/framework/src/main/java/org/elasticsearch/repositories/ESBlobStoreContainerTestCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public void testVerifyOverwriteFails() throws IOException {
142142
}
143143
}
144144

145-
private void writeBlob(final BlobContainer container, final String blobName, final BytesArray bytesArray) throws IOException {
145+
protected void writeBlob(final BlobContainer container, final String blobName, final BytesArray bytesArray) throws IOException {
146146
try (InputStream stream = bytesArray.streamInput()) {
147147
container.writeBlob(blobName, stream, bytesArray.length());
148148
}

test/framework/src/main/java/org/elasticsearch/repositories/ESBlobStoreTestCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static byte[] randomBytes(int length) {
7878
return data;
7979
}
8080

81-
private static void writeBlob(BlobContainer container, String blobName, BytesArray bytesArray) throws IOException {
81+
protected static void writeBlob(BlobContainer container, String blobName, BytesArray bytesArray) throws IOException {
8282
try (InputStream stream = bytesArray.streamInput()) {
8383
container.writeBlob(blobName, stream, bytesArray.length());
8484
}

0 commit comments

Comments
 (0)