Skip to content

Commit 489bd71

Browse files
committed
Do not create directories if repository is readonly (#21495)
For FsBlobStore and HdfsBlobStore, if the repository is read only, the blob store should aware the read only setting and do not create directories even it's not exists. Do not mkdirs if HDFS repository is read only
1 parent f9e755f commit 489bd71

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)