Skip to content

Commit 5962d16

Browse files
authored
feat: export/import functionality (#153)
1 parent 4ea9184 commit 5962d16

File tree

74 files changed

+3604
-61
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+3604
-61
lines changed

docs/configuration.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,13 @@ TLS and routing rules are generated automatically from `app.nim.deploy.cluster-h
257257
| `app.resources.max-memory-in-mb` | `RESOURCES_MAX_MEMORY_IN_MB` | `100000` | No | - | Maximum allowed value for memory resource (in mb) |
258258
| `app.resources.max-nvidia-gpu` | `RESOURCES_MAX_NVIDIA_GPU` | `5` | No | - | Maximum allowed value for nvidia.com/gpu resource |
259259

260+
### Export/import Configuration
261+
262+
| Property | Environment Variable | Default Value | Required | Applied when | Description |
263+
|-------------------------------|---------------------------|---------------------------------|----------|---------------|-----------------------------------------------|
264+
| `app.config.export.file-name` | `CONFIG_EXPORT_FILE_NAME` | `dm-config.json` | No | - | Name of JSON file with exported components |
265+
| `app.config.export.zip-name` | `CONFIG_EXPORT_ZIP_NAME` | `deployment-manager-config.zip` | No | - | Name of ZIP archive containing exported files |
266+
260267
### HTTP Client Configuration
261268

262269
Used by:

docs/rest-collection/dm_postman_collection.json

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,77 @@
14681468
}
14691469
]
14701470
},
1471+
{
1472+
"name": "config",
1473+
"item": [
1474+
{
1475+
"name": "export",
1476+
"request": {
1477+
"method": "POST",
1478+
"header": [],
1479+
"body": {
1480+
"mode": "raw",
1481+
"raw": "{\n \"$type\": \"custom\",\n \"addSecrets\": false,\n \"addGlobalImageBuildDomainWhitelist\": true,\n \"components\": [\n {\n \"name\": \"{{DEPLOYMENT_NAME}}\",\n \"type\": \"MCP_DEPLOYMENT\"\n }\n ]\n}",
1482+
"options": {
1483+
"raw": {
1484+
"language": "json"
1485+
}
1486+
}
1487+
},
1488+
"url": {
1489+
"raw": "{{HOST}}/api/v1/configs/export",
1490+
"host": [
1491+
"{{HOST}}"
1492+
],
1493+
"path": [
1494+
"api",
1495+
"v1",
1496+
"configs",
1497+
"export"
1498+
]
1499+
}
1500+
},
1501+
"response": []
1502+
},
1503+
{
1504+
"name": "import",
1505+
"request": {
1506+
"method": "POST",
1507+
"header": [],
1508+
"body": {
1509+
"mode": "formdata",
1510+
"formdata": [
1511+
{
1512+
"key": "file",
1513+
"type": "file",
1514+
"uuid": "ef065c92-6627-4867-8878-c144fbd3ad87",
1515+
"value": null
1516+
}
1517+
]
1518+
},
1519+
"url": {
1520+
"raw": "{{HOST}}/api/v1/configs/import?resolutionPolicy=SKIP_IF_EXISTS",
1521+
"host": [
1522+
"{{HOST}}"
1523+
],
1524+
"path": [
1525+
"api",
1526+
"v1",
1527+
"configs",
1528+
"import"
1529+
],
1530+
"query": [
1531+
{
1532+
"key": "resolutionPolicy",
1533+
"value": "SKIP_IF_EXISTS"
1534+
}
1535+
]
1536+
}
1537+
},
1538+
"response": []
1539+
}
1540+
]
1541+
},
14711542
{
14721543
"name": "healthcheck",
14731544
"protocolProfileBehavior": {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.epam.aidial.deployment.manager.configuration;
2+
3+
import lombok.Data;
4+
import org.springframework.boot.context.properties.ConfigurationProperties;
5+
import org.springframework.context.annotation.Configuration;
6+
7+
@Data
8+
@Configuration
9+
@ConfigurationProperties(prefix = "app.config.export")
10+
public class ConfigExportProperties {
11+
private String fileName;
12+
private String zipName;
13+
}
Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,69 @@
11
package com.epam.aidial.deployment.manager.configuration;
22

3+
import com.epam.aidial.deployment.manager.configuration.export.DeploymentExportMixIn;
4+
import com.epam.aidial.deployment.manager.configuration.export.ImageDefinitionExportMixIn;
5+
import com.epam.aidial.deployment.manager.configuration.export.SensitiveEnvVarExportMixIn;
6+
import com.epam.aidial.deployment.manager.model.ImageDefinition;
7+
import com.epam.aidial.deployment.manager.model.SensitiveEnvVar;
8+
import com.epam.aidial.deployment.manager.model.deployment.Deployment;
39
import com.fasterxml.jackson.annotation.JsonInclude;
10+
import com.fasterxml.jackson.core.JsonParser;
411
import com.fasterxml.jackson.databind.DeserializationFeature;
512
import com.fasterxml.jackson.databind.MapperFeature;
613
import com.fasterxml.jackson.databind.SerializationFeature;
714
import com.fasterxml.jackson.databind.cfg.EnumFeature;
815
import com.fasterxml.jackson.databind.json.JsonMapper;
916
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
17+
import org.springframework.beans.factory.annotation.Qualifier;
1018
import org.springframework.context.annotation.Bean;
1119
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.context.annotation.Primary;
1221

1322
@Configuration
1423
public class JsonMapperConfiguration {
1524

1625
@Bean
26+
@Primary
1727
public JsonMapper getJsonMapper() {
1828
return createJsonMapper();
1929
}
2030

31+
@Bean
32+
@Qualifier("prettyJsonMapper")
33+
public JsonMapper getPrettyJsonMapper() {
34+
return createPrettyJsonMapper();
35+
}
36+
37+
@Bean
38+
@Qualifier("exportJsonMapper")
39+
public JsonMapper getExportJsonMapper() {
40+
JsonMapper mapper = createPrettyJsonMapper();
41+
mapper.addMixIn(ImageDefinition.class, ImageDefinitionExportMixIn.class);
42+
mapper.addMixIn(Deployment.class, DeploymentExportMixIn.class);
43+
mapper.addMixIn(SensitiveEnvVar.class, SensitiveEnvVarExportMixIn.class);
44+
return mapper;
45+
}
46+
2147
public static JsonMapper createJsonMapper() {
48+
return createDefaultJsonMapperBuilder().build();
49+
}
50+
51+
public static JsonMapper createPrettyJsonMapper() {
52+
return createDefaultJsonMapperBuilder()
53+
.enable(SerializationFeature.INDENT_OUTPUT)
54+
.build();
55+
}
56+
57+
private static JsonMapper.Builder createDefaultJsonMapperBuilder() {
2258
return JsonMapper.builder()
2359
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
2460
.enable(EnumFeature.WRITE_ENUMS_TO_LOWERCASE)
2561
.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
2662
.disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)
2763
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
64+
.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false)
2865
.serializationInclusion(JsonInclude.Include.NON_NULL)
29-
.addModule(new JavaTimeModule())
30-
.build();
66+
.addModule(new JavaTimeModule());
3167
}
3268

3369
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.epam.aidial.deployment.manager.configuration.export;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnore;
4+
5+
import java.time.Instant;
6+
import java.util.UUID;
7+
8+
/**
9+
* Mix-in for Deployment export. Fields which have getters marked with @JsonIgnore will be excluded from export.
10+
*/
11+
public abstract class DeploymentExportMixIn {
12+
13+
@JsonIgnore
14+
abstract String getUrl();
15+
16+
@JsonIgnore
17+
abstract Object getStatus();
18+
19+
@JsonIgnore
20+
abstract String getAuthor();
21+
22+
@JsonIgnore
23+
abstract Instant getCreatedAt();
24+
25+
@JsonIgnore
26+
abstract Instant getUpdatedAt();
27+
28+
@JsonIgnore
29+
abstract UUID getImageDefinitionId();
30+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.epam.aidial.deployment.manager.configuration.export;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnore;
4+
5+
import java.time.Instant;
6+
import java.util.List;
7+
import java.util.UUID;
8+
9+
/**
10+
* Mix-in for Image Definition export. Fields which have getters marked with @JsonIgnore will be excluded from export.
11+
*/
12+
public abstract class ImageDefinitionExportMixIn {
13+
14+
@JsonIgnore
15+
abstract UUID getId();
16+
17+
@JsonIgnore
18+
abstract String getImageName();
19+
20+
@JsonIgnore
21+
abstract Object getBuildStatus();
22+
23+
@JsonIgnore
24+
abstract List<String> getBuildLogs();
25+
26+
@JsonIgnore
27+
abstract Instant getBuiltAt();
28+
29+
@JsonIgnore
30+
abstract String getAuthor();
31+
32+
@JsonIgnore
33+
abstract Instant getCreatedAt();
34+
35+
@JsonIgnore
36+
abstract Instant getUpdatedAt();
37+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.epam.aidial.deployment.manager.configuration.export;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnore;
4+
5+
/**
6+
* Mix-in for SensitiveEnvVar export: exclude k8sSecretName, k8sSecretKey (generated on deployment creation).
7+
* Value is included or null depending on addSecrets (handled in exporter).
8+
*/
9+
public abstract class SensitiveEnvVarExportMixIn {
10+
11+
@JsonIgnore
12+
abstract String getK8sSecretName();
13+
14+
@JsonIgnore
15+
abstract String getK8sSecretKey();
16+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.epam.aidial.deployment.manager.dao.entity;
2+
3+
public enum PersistenceImageType {
4+
MCP,
5+
ADAPTER,
6+
INTERCEPTOR,
7+
}

src/main/java/com/epam/aidial/deployment/manager/dao/entity/deployment/DeploymentEntity.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.epam.aidial.deployment.manager.dao.entity.PersistenceDeploymentMetadata;
44
import com.epam.aidial.deployment.manager.dao.entity.PersistenceDeploymentStatus;
55
import com.epam.aidial.deployment.manager.dao.entity.PersistenceEnvVar;
6+
import com.epam.aidial.deployment.manager.dao.entity.PersistenceImageType;
67
import com.epam.aidial.deployment.manager.dao.entity.PersistenceResources;
78
import com.epam.aidial.deployment.manager.dao.entity.probe.PersistenceProbeProperties;
89
import jakarta.persistence.Column;
@@ -41,6 +42,10 @@ public class DeploymentEntity {
4142
@Column(name = "image_definition_id")
4243
private UUID imageDefinitionId;
4344

45+
@Column(name = "image_definition_type")
46+
@Enumerated(EnumType.STRING)
47+
private PersistenceImageType imageDefinitionType;
48+
4449
@Column(name = "image_definition_name")
4550
private String imageDefinitionName;
4651

src/main/java/com/epam/aidial/deployment/manager/dao/jpa/ImageDefinitionJpaRepository.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ List<ImageDefinitionEntity> findAllByNameAndType(
7070
@Param("type") Class<? extends ImageDefinitionEntity> type
7171
);
7272

73+
@Query("SELECT i FROM ImageDefinitionEntity i WHERE i.name = :name AND TYPE(i) = :type AND i.version = :version")
74+
Optional<ImageDefinitionEntity> findByNameAndTypeAndVersion(
75+
@Param("name") String name,
76+
@Param("type") Class<? extends ImageDefinitionEntity> type,
77+
@Param("version") String version
78+
);
79+
7380
List<ImageDefinitionEntity> findAllByName(@Param("name") String name);
7481

7582
}

0 commit comments

Comments
 (0)