diff --git a/Dockerfile b/Dockerfile
index ae8b103d3..be3b962df 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -17,7 +17,6 @@ FROM eclipse-temurin:17-jdk-alpine
# TODO remove the fix once a new version is released
RUN apk update && apk upgrade --no-cache libcrypto3 libssl3
-ENV JAVA_OPTS="-Dgflog.config=/app/config/gflog.xml"
ENV OTEL_TRACES_EXPORTER="none"
ENV OTEL_METRICS_EXPORTER="none"
ENV OTEL_LOGS_EXPORTER="none"
@@ -27,7 +26,6 @@ WORKDIR /app
RUN adduser -u 1001 --disabled-password --gecos "" appuser
COPY --from=builder --chown=appuser:appuser /build/ .
-COPY --chown=appuser:appuser ./config/* /app/config/
RUN mkdir /app/log && chown -R appuser:appuser /app
USER appuser
diff --git a/README.md b/README.md
index de55c6aa9..1a03fa7a0 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,6 @@ Static settings are used on startup and cannot be changed while application is r
| storage.createBucket | false | Indicates whether bucket should be created on start-up
| encryption.password | - | Password used for AES encryption
| encryption.salt | - | Salt used for AES encryption
-| encryption.salt | - | Salt used for AES encryption
| resources.maxSize | 1048576 | Max allowed size in bytes for a resource
| resources.syncPeriod | 60000 | Period in milliseconds, how frequently check for resources to sync
| resources.syncDelay | 120000 | Delay in milliseconds for a resource to be written back in object storage after last modification
@@ -76,7 +75,7 @@ maxmemory 4G
maxmemory-policy volatile-lfu
```
-Note: Redis will be strictly required in the upcoming releases 0.7+.
+Note: Redis will be strictly required in the upcoming releases 0.8+.
### Dynamic settings
diff --git a/config/gflog.xml b/config/gflog.xml
deleted file mode 100644
index 4f11fdef1..000000000
--- a/config/gflog.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/java/com/epam/aidial/core/config/FileConfigStore.java b/src/main/java/com/epam/aidial/core/config/FileConfigStore.java
index a71ebfb41..a685d2e97 100644
--- a/src/main/java/com/epam/aidial/core/config/FileConfigStore.java
+++ b/src/main/java/com/epam/aidial/core/config/FileConfigStore.java
@@ -108,15 +108,11 @@ private void load(boolean fail) {
}
private Config loadConfig() throws Exception {
- JsonNode tree = null;
+ JsonNode tree = ProxyUtil.MAPPER.createObjectNode();
for (String path : paths) {
try (InputStream stream = openStream(path)) {
- if (tree == null) {
- tree = ProxyUtil.MAPPER.readTree(stream);
- } else {
- tree = ProxyUtil.MAPPER.readerForUpdating(tree).readTree(stream);
- }
+ tree = ProxyUtil.MAPPER.readerForUpdating(tree).readTree(stream);
}
}
diff --git a/src/main/java/com/epam/aidial/core/controller/ResourceController.java b/src/main/java/com/epam/aidial/core/controller/ResourceController.java
index 71818ee1f..b49ee99dd 100644
--- a/src/main/java/com/epam/aidial/core/controller/ResourceController.java
+++ b/src/main/java/com/epam/aidial/core/controller/ResourceController.java
@@ -9,6 +9,7 @@
import com.epam.aidial.core.util.ProxyUtil;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
+import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import lombok.extern.slf4j.Slf4j;
@@ -106,6 +107,13 @@ private Future> putResource(ResourceDescription descriptor) {
return context.respond(HttpStatus.REQUEST_ENTITY_TOO_LARGE, message);
}
+ String ifNoneMatch = context.getRequest().getHeader(HttpHeaders.IF_NONE_MATCH);
+ boolean overwrite = (ifNoneMatch == null);
+
+ if (ifNoneMatch != null && !ifNoneMatch.equals("*")) {
+ return context.respond(HttpStatus.BAD_REQUEST, "only header if-none-match=* is supported");
+ }
+
return context.getRequest().body().compose(bytes -> {
if (bytes.length() > contentLimit) {
String message = "Resource size: %s exceeds max limit: %s".formatted(bytes.length(), contentLimit);
@@ -113,9 +121,15 @@ private Future> putResource(ResourceDescription descriptor) {
}
String body = bytes.toString(StandardCharsets.UTF_8);
- return vertx.executeBlocking(() -> service.putResource(descriptor, body));
+ return vertx.executeBlocking(() -> service.putResource(descriptor, body, overwrite));
+ })
+ .onSuccess((metadata) -> {
+ if (metadata == null) {
+ context.respond(HttpStatus.CONFLICT, "Resource already exists: " + descriptor.getUrl());
+ } else {
+ context.respond(HttpStatus.OK, metadata);
+ }
})
- .onSuccess((metadata) -> context.respond(HttpStatus.OK, metadata))
.onFailure(error -> {
if (error instanceof HttpException exception) {
context.respond(exception.getStatus(), exception.getMessage());
diff --git a/src/main/java/com/epam/aidial/core/service/ResourceService.java b/src/main/java/com/epam/aidial/core/service/ResourceService.java
index 0cd8256eb..6361112c3 100644
--- a/src/main/java/com/epam/aidial/core/service/ResourceService.java
+++ b/src/main/java/com/epam/aidial/core/service/ResourceService.java
@@ -67,11 +67,11 @@ public ResourceService(Vertx vertx,
}
/**
- * @param maxSize - max allowed size in bytes for a resource.
- * @param syncPeriod - period in milliseconds, how frequently check for resources to sync.
- * @param syncDelay - delay in milliseconds for a resource to be written back in object storage after last modification.
- * @param syncBatch - how many resources to sync in one go.
- * @param cacheExpiration - expiration in milliseconds for synced resources in Redis.
+ * @param maxSize - max allowed size in bytes for a resource.
+ * @param syncPeriod - period in milliseconds, how frequently check for resources to sync.
+ * @param syncDelay - delay in milliseconds for a resource to be written back in object storage after last modification.
+ * @param syncBatch - how many resources to sync in one go.
+ * @param cacheExpiration - expiration in milliseconds for synced resources in Redis.
* @param compressionMinSize - compress resources with gzip if their size in bytes more or equal to this value.
*/
public ResourceService(Vertx vertx,
@@ -192,11 +192,12 @@ public String getResource(ResourceDescription descriptor, boolean lock) {
return result.exists ? result.body : null;
}
- public ResourceItemMetadata putResource(ResourceDescription descriptor, String body) {
- return putResource(descriptor, body, true);
+ public ResourceItemMetadata putResource(ResourceDescription descriptor, String body, boolean overwrite) {
+ return putResource(descriptor, body, overwrite, true);
}
- public ResourceItemMetadata putResource(ResourceDescription descriptor, String body, boolean lock) {
+ public ResourceItemMetadata putResource(ResourceDescription descriptor, String body,
+ boolean overwrite, boolean lock) {
String redisKey = redisKey(descriptor);
String blobKey = blobKey(descriptor);
@@ -206,6 +207,10 @@ public ResourceItemMetadata putResource(ResourceDescription descriptor, String b
result = blobGet(blobKey, false);
}
+ if (result.exists && !overwrite) {
+ return null;
+ }
+
long updatedAt = time();
long createdAt = result.exists ? result.createdAt : updatedAt;
redisPut(redisKey, new Result(body, createdAt, updatedAt, false, true));
@@ -224,7 +229,7 @@ public void computeResource(ResourceDescription descriptor, Function