diff --git a/docs/modules/onecx-product-store-operator/pages/onecx-product-store-operator-docs.adoc b/docs/modules/onecx-product-store-operator/pages/onecx-product-store-operator-docs.adoc
index cb16c0e..890c83a 100644
--- a/docs/modules/onecx-product-store-operator/pages/onecx-product-store-operator-docs.adoc
+++ b/docs/modules/onecx-product-store-operator/pages/onecx-product-store-operator-docs.adoc
@@ -23,6 +23,10 @@ quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_product_v1_y
quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_product_v1_yaml.base-package=gen.org.tkit.onecx.product.store.product.v1
quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_product_v1_yaml.return-response=true
quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_product_v1_yaml.enable-security-generation=false
+quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_slot_v1_yaml.config-key=product_store_client
+quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_slot_v1_yaml.base-package=gen.org.tkit.onecx.product.store.slot.v1
+quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_slot_v1_yaml.return-response=true
+quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_slot_v1_yaml.enable-security-generation=false
----
====
@@ -63,5 +67,20 @@ app:
kcConfig:
defaultClientScopes: [ ocx-ps-product:write ]
+----
+
+
+ fieldRef:
+ fieldPath: metadata.namespace
+ serviceAccount:
+ enabled: true
+ operator:
+ keycloak:
+ client:
+ enabled: true
+ spec:
+ kcConfig:
+ defaultClientScopes: [ ocx-ps-product:write ]
+
----
diff --git a/docs/modules/onecx-product-store-operator/pages/onecx-product-store-operator-extensions.adoc b/docs/modules/onecx-product-store-operator/pages/onecx-product-store-operator-extensions.adoc
index f57a7e6..6d94344 100644
--- a/docs/modules/onecx-product-store-operator/pages/onecx-product-store-operator-extensions.adoc
+++ b/docs/modules/onecx-product-store-operator/pages/onecx-product-store-operator-extensions.adoc
@@ -72,7 +72,7 @@ h| Version
| https://onecx.github.io/docs/onecx-quarkus/current/onecx-quarkus/onecx-core.html[Link]
|
-| 0.32.0
+| 0.34.0
| quarkus-smallrye-health
@@ -91,13 +91,13 @@ h| Version
|
|
-| 6.7.3
+| 6.8.3
| quarkus-operator-sdk
|
|
-| 6.7.3
+| 6.8.3
| quarkus-oidc-client
diff --git a/pom.xml b/pom.xml
index 9fb969d..a7c91eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -114,18 +114,32 @@
download-maven-plugin
+ product-operator-api
generate-resources
wget
+
+ https://raw.githubusercontent.com/onecx/onecx-product-store-svc/main/src/main/openapi/onecx-product-store-operator-product-v1.yaml
+ target/tmp/openapi
+ onecx-product-store-operator-product-v1.yaml
+ true
+
+
+
+ slot-operator-api
+ generate-resources
+
+ wget
+
+
+ https://raw.githubusercontent.com/onecx/onecx-product-store-svc/main/src/main/openapi/onecx-product-store-operator-slot-v1.yaml
+ target/tmp/openapi
+ onecx-product-store-operator-slot-v1.yaml
+ true
+
-
- https://raw.githubusercontent.com/onecx/onecx-product-store-svc/main/src/main/openapi/onecx-product-store-operator-product-v1.yaml
- target/tmp/openapi
- onecx-product-store-operator-product-v1.yaml
- true
-
diff --git a/src/main/helm/templates/role.yaml b/src/main/helm/templates/role.yaml
index 2145ec9..40dee34 100644
--- a/src/main/helm/templates/role.yaml
+++ b/src/main/helm/templates/role.yaml
@@ -9,6 +9,9 @@ rules:
- products
- products/status
- products/finalizers
+ - slots
+ - slots/status
+ - slots/finalizers
verbs:
- get
- list
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/LeaderConfiguration.java b/src/main/java/org/tkit/onecx/product/store/operator/LeaderConfiguration.java
index 1299fa5..ae2a8ea 100644
--- a/src/main/java/org/tkit/onecx/product/store/operator/LeaderConfiguration.java
+++ b/src/main/java/org/tkit/onecx/product/store/operator/LeaderConfiguration.java
@@ -10,4 +10,4 @@ public class LeaderConfiguration extends LeaderElectionConfiguration {
public LeaderConfiguration(OperatorConfig config) {
super(config.leaderElectionConfig().leaseName());
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/OperatorConfig.java b/src/main/java/org/tkit/onecx/product/store/operator/OperatorConfig.java
index f49c663..47df7ef 100644
--- a/src/main/java/org/tkit/onecx/product/store/operator/OperatorConfig.java
+++ b/src/main/java/org/tkit/onecx/product/store/operator/OperatorConfig.java
@@ -32,4 +32,4 @@ interface LeaderElectionConfig {
@WithDefault("onecx-product-store-operator-lease")
String leaseName();
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/client/ProductStoreService.java b/src/main/java/org/tkit/onecx/product/store/operator/client/ProductStoreService.java
index 16fcdcb..5f40b6d 100644
--- a/src/main/java/org/tkit/onecx/product/store/operator/client/ProductStoreService.java
+++ b/src/main/java/org/tkit/onecx/product/store/operator/client/ProductStoreService.java
@@ -6,12 +6,16 @@
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.tkit.onecx.product.store.operator.Product;
-import org.tkit.onecx.product.store.operator.ProductSpec;
import org.tkit.onecx.product.store.operator.client.mappers.ProductStoreMapper;
+import org.tkit.onecx.product.store.operator.product.Product;
+import org.tkit.onecx.product.store.operator.product.ProductSpec;
+import org.tkit.onecx.product.store.operator.slot.Slot;
+import org.tkit.onecx.product.store.operator.slot.SlotSpec;
import gen.org.tkit.onecx.product.store.product.v1.api.OperatorProductApi;
import gen.org.tkit.onecx.product.store.product.v1.model.UpdateProductRequest;
+import gen.org.tkit.onecx.product.store.slot.v1.api.OperatorSlotApi;
+import gen.org.tkit.onecx.product.store.slot.v1.model.UpdateSlotRequest;
@ApplicationScoped
public class ProductStoreService {
@@ -25,6 +29,10 @@ public class ProductStoreService {
@RestClient
OperatorProductApi client;
+ @Inject
+ @RestClient
+ OperatorSlotApi slotClient;
+
public int updateProduct(Product product) {
ProductSpec spec = product.getSpec();
UpdateProductRequest dto = mapper.map(spec);
@@ -34,4 +42,13 @@ public int updateProduct(Product product) {
}
}
+ public int updateSlot(Slot slot) {
+ SlotSpec spec = slot.getSpec();
+ UpdateSlotRequest dto = mapper.map(spec);
+ try (var response = slotClient.createOrUpdateSlot(spec.getProductName(), spec.getAppId(), dto)) {
+ log.info("Update micro-fronted response {}", response.getStatus());
+ return response.getStatus();
+ }
+ }
+
}
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/client/mappers/ProductStoreMapper.java b/src/main/java/org/tkit/onecx/product/store/operator/client/mappers/ProductStoreMapper.java
index 5a42206..02724b9 100644
--- a/src/main/java/org/tkit/onecx/product/store/operator/client/mappers/ProductStoreMapper.java
+++ b/src/main/java/org/tkit/onecx/product/store/operator/client/mappers/ProductStoreMapper.java
@@ -2,13 +2,18 @@
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
-import org.tkit.onecx.product.store.operator.ProductSpec;
+import org.tkit.onecx.product.store.operator.product.ProductSpec;
+import org.tkit.onecx.product.store.operator.slot.SlotSpec;
import gen.org.tkit.onecx.product.store.product.v1.model.UpdateProductRequest;
+import gen.org.tkit.onecx.product.store.slot.v1.model.UpdateSlotRequest;
@Mapper
public interface ProductStoreMapper {
@Mapping(target = "undeployed", constant = "false")
UpdateProductRequest map(ProductSpec spec);
+
+ @Mapping(target = "undeployed", constant = "false")
+ UpdateSlotRequest map(SlotSpec spec);
}
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/Product.java b/src/main/java/org/tkit/onecx/product/store/operator/product/Product.java
similarity index 86%
rename from src/main/java/org/tkit/onecx/product/store/operator/Product.java
rename to src/main/java/org/tkit/onecx/product/store/operator/product/Product.java
index 0f5ec88..1669850 100644
--- a/src/main/java/org/tkit/onecx/product/store/operator/Product.java
+++ b/src/main/java/org/tkit/onecx/product/store/operator/product/Product.java
@@ -1,4 +1,4 @@
-package org.tkit.onecx.product.store.operator;
+package org.tkit.onecx.product.store.operator.product;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/ProductController.java b/src/main/java/org/tkit/onecx/product/store/operator/product/ProductController.java
similarity index 98%
rename from src/main/java/org/tkit/onecx/product/store/operator/ProductController.java
rename to src/main/java/org/tkit/onecx/product/store/operator/product/ProductController.java
index 8ca9c5c..2014037 100644
--- a/src/main/java/org/tkit/onecx/product/store/operator/ProductController.java
+++ b/src/main/java/org/tkit/onecx/product/store/operator/product/ProductController.java
@@ -1,4 +1,4 @@
-package org.tkit.onecx.product.store.operator;
+package org.tkit.onecx.product.store.operator.product;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/ProductSpec.java b/src/main/java/org/tkit/onecx/product/store/operator/product/ProductSpec.java
similarity index 97%
rename from src/main/java/org/tkit/onecx/product/store/operator/ProductSpec.java
rename to src/main/java/org/tkit/onecx/product/store/operator/product/ProductSpec.java
index e411328..4a3013e 100644
--- a/src/main/java/org/tkit/onecx/product/store/operator/ProductSpec.java
+++ b/src/main/java/org/tkit/onecx/product/store/operator/product/ProductSpec.java
@@ -1,4 +1,4 @@
-package org.tkit.onecx.product.store.operator;
+package org.tkit.onecx.product.store.operator.product;
import java.util.Set;
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/ProductStatus.java b/src/main/java/org/tkit/onecx/product/store/operator/product/ProductStatus.java
similarity index 95%
rename from src/main/java/org/tkit/onecx/product/store/operator/ProductStatus.java
rename to src/main/java/org/tkit/onecx/product/store/operator/product/ProductStatus.java
index 5f1d2e7..a02bb7d 100644
--- a/src/main/java/org/tkit/onecx/product/store/operator/ProductStatus.java
+++ b/src/main/java/org/tkit/onecx/product/store/operator/product/ProductStatus.java
@@ -1,4 +1,4 @@
-package org.tkit.onecx.product.store.operator;
+package org.tkit.onecx.product.store.operator.product;
import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/slot/Slot.java b/src/main/java/org/tkit/onecx/product/store/operator/slot/Slot.java
new file mode 100644
index 0000000..51c0b0d
--- /dev/null
+++ b/src/main/java/org/tkit/onecx/product/store/operator/slot/Slot.java
@@ -0,0 +1,11 @@
+package org.tkit.onecx.product.store.operator.slot;
+
+import io.fabric8.kubernetes.api.model.Namespaced;
+import io.fabric8.kubernetes.client.CustomResource;
+import io.fabric8.kubernetes.model.annotation.Group;
+import io.fabric8.kubernetes.model.annotation.Version;
+
+@Version("v1")
+@Group("onecx.tkit.org")
+public class Slot extends CustomResource implements Namespaced {
+}
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotController.java b/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotController.java
new file mode 100644
index 0000000..dc91726
--- /dev/null
+++ b/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotController.java
@@ -0,0 +1,96 @@
+package org.tkit.onecx.product.store.operator.slot;
+
+import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_CURRENT_NAMESPACE;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.WebApplicationException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.tkit.onecx.product.store.operator.client.ProductStoreService;
+
+import io.javaoperatorsdk.operator.api.reconciler.*;
+import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter;
+import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter;
+
+@ControllerConfiguration(name = "slot", namespaces = WATCH_CURRENT_NAMESPACE, onAddFilter = SlotController.SlotAddFilter.class, onUpdateFilter = SlotController.SlotUpdateFilter.class)
+public class SlotController implements Reconciler, ErrorStatusHandler {
+
+ private static final Logger log = LoggerFactory.getLogger(SlotController.class);
+
+ @Inject
+ ProductStoreService service;
+
+ @Override
+ public UpdateControl reconcile(Slot slot, Context context)
+ throws Exception {
+
+ String appId = slot.getSpec().getAppId();
+ String productName = slot.getSpec().getProductName();
+ String name = slot.getSpec().getName();
+
+ log.info("Reconcile microservice: {} for product: {}, name: {}", appId, productName, name);
+ int responseCode = service.updateSlot(slot);
+
+ updateStatusPojo(slot, responseCode);
+ log.info("Microservice '{}' reconciled - updating status", slot.getMetadata().getName());
+ return UpdateControl.updateStatus(slot);
+
+ }
+
+ @Override
+ public ErrorStatusUpdateControl updateErrorStatus(Slot slot,
+ Context context, Exception e) {
+
+ int responseCode = -1;
+ if (e.getCause() instanceof WebApplicationException re) {
+ responseCode = re.getResponse().getStatus();
+ }
+
+ log.error("Error reconcile resource", e);
+ var status = new SlotStatus();
+ status.setRequestProductName(null);
+ status.setRequestAppId(null);
+ status.setRequestName(null);
+ status.setResponseCode(responseCode);
+ status.setStatus(SlotStatus.Status.ERROR);
+ status.setMessage(e.getMessage());
+ slot.setStatus(status);
+ return ErrorStatusUpdateControl.updateStatus(slot);
+ }
+
+ private void updateStatusPojo(Slot slot, int responseCode) {
+ var result = new SlotStatus();
+ var spec = slot.getSpec();
+ result.setRequestProductName(spec.getProductName());
+ result.setRequestAppId(spec.getAppId());
+ result.setRequestName(spec.getName());
+ result.setResponseCode(responseCode);
+ var status = switch (responseCode) {
+ case 201:
+ yield SlotStatus.Status.CREATED;
+ case 200:
+ yield SlotStatus.Status.UPDATED;
+ default:
+ yield SlotStatus.Status.UNDEFINED;
+ };
+ result.setStatus(status);
+ slot.setStatus(result);
+ }
+
+ public static class SlotAddFilter implements OnAddFilter {
+
+ @Override
+ public boolean accept(Slot resource) {
+ return resource.getSpec() != null;
+ }
+ }
+
+ public static class SlotUpdateFilter implements OnUpdateFilter {
+
+ @Override
+ public boolean accept(Slot newResource, Slot oldResource) {
+ return newResource.getSpec() != null;
+ }
+ }
+}
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotSpec.java b/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotSpec.java
new file mode 100644
index 0000000..c4c96a8
--- /dev/null
+++ b/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotSpec.java
@@ -0,0 +1,68 @@
+package org.tkit.onecx.product.store.operator.slot;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class SlotSpec {
+
+ @JsonProperty("appId")
+ private String appId;
+
+ @JsonProperty("productName")
+ private String productName;
+
+ @JsonProperty("description")
+ private String description;
+
+ @JsonProperty("name")
+ private String name;
+
+ @JsonProperty(value = "deprecated")
+ private boolean deprecated = false;
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public String getProductName() {
+ return productName;
+ }
+
+ public void setProductName(String productName) {
+ this.productName = productName;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setDeprecated(boolean deprecated) {
+ this.deprecated = deprecated;
+ }
+
+ public boolean isDeprecated() {
+ return deprecated;
+ }
+
+ @Override
+ public String toString() {
+ return "SlotSpec{" + "appId='" + appId + "', productName='" + productName + "', name='" + name + "'}";
+ }
+}
diff --git a/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotStatus.java b/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotStatus.java
new file mode 100644
index 0000000..d4e2198
--- /dev/null
+++ b/src/main/java/org/tkit/onecx/product/store/operator/slot/SlotStatus.java
@@ -0,0 +1,86 @@
+package org.tkit.onecx.product.store.operator.slot;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
+
+public class SlotStatus extends ObservedGenerationAwareStatus {
+
+ @JsonProperty("productName")
+ private String requestProductName;
+
+ @JsonProperty("appId")
+ private String requestAppId;
+
+ @JsonProperty("name")
+ private String requestName;
+
+ @JsonProperty("responseCode")
+ private int responseCode;
+
+ @JsonProperty("status")
+ private Status status;
+
+ @JsonProperty("message")
+ private String message;
+
+ public enum Status {
+
+ ERROR,
+
+ CREATED,
+
+ UPDATED,
+
+ UNDEFINED;
+ }
+
+ public String getRequestProductName() {
+ return requestProductName;
+ }
+
+ public void setRequestProductName(String requestProductName) {
+ this.requestProductName = requestProductName;
+ }
+
+ public String getRequestAppId() {
+ return requestAppId;
+ }
+
+ public void setRequestAppId(String requestAppId) {
+ this.requestAppId = requestAppId;
+ }
+
+ public String getRequestName() {
+ return requestName;
+ }
+
+ public void setRequestName(String requestName) {
+ this.requestName = requestName;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public void setResponseCode(int responseCode) {
+ this.responseCode = responseCode;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 9f31ef3..983d0dd 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -17,11 +17,18 @@ quarkus.kubernetes-client.devservices.override-kubeconfig=true
quarkus.operator-sdk.crd.validate=false
quarkus.operator-sdk.helm.enabled=true
quarkus.openapi-generator.codegen.input-base-dir=target/tmp/openapi
+
+# PRODUCT
quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_product_v1_yaml.config-key=product_store_client
quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_product_v1_yaml.base-package=gen.org.tkit.onecx.product.store.product.v1
quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_product_v1_yaml.return-response=true
quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_product_v1_yaml.enable-security-generation=false
+# SLOT
+quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_slot_v1_yaml.config-key=product_store_client
+quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_slot_v1_yaml.base-package=gen.org.tkit.onecx.product.store.slot.v1
+quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_slot_v1_yaml.return-response=true
+quarkus.openapi-generator.codegen.spec.onecx_product_store_operator_slot_v1_yaml.enable-security-generation=false
# TEST
%test.quarkus.mockserver.devservices.config-class-path=true
diff --git a/src/test/java/org/tkit/onecx/product/store/operator/LeaderConfigurationTest.java b/src/test/java/org/tkit/onecx/product/store/operator/LeaderConfigurationTest.java
index d5c261d..0ce8e9a 100644
--- a/src/test/java/org/tkit/onecx/product/store/operator/LeaderConfigurationTest.java
+++ b/src/test/java/org/tkit/onecx/product/store/operator/LeaderConfigurationTest.java
@@ -23,6 +23,7 @@ void testLeaderConfiguration() {
assertThat(dataConfig).isNotNull();
assertThat(dataConfig.leaderElectionConfig()).isNotNull();
assertThat(leaderConfiguration).isNotNull();
- assertThat(leaderConfiguration.getLeaseName()).isNotNull().isEqualTo(dataConfig.leaderElectionConfig().leaseName());
+ assertThat(leaderConfiguration.getLeaseName()).isNotNull()
+ .isEqualTo(dataConfig.leaderElectionConfig().leaseName());
}
}
diff --git a/src/test/java/org/tkit/onecx/product/store/operator/ProductControllerResponseTest.java b/src/test/java/org/tkit/onecx/product/store/operator/ProductControllerResponseTest.java
index cf979c0..e5b10e7 100644
--- a/src/test/java/org/tkit/onecx/product/store/operator/ProductControllerResponseTest.java
+++ b/src/test/java/org/tkit/onecx/product/store/operator/ProductControllerResponseTest.java
@@ -9,6 +9,10 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.tkit.onecx.product.store.operator.client.ProductStoreService;
+import org.tkit.onecx.product.store.operator.product.Product;
+import org.tkit.onecx.product.store.operator.product.ProductController;
+import org.tkit.onecx.product.store.operator.product.ProductSpec;
+import org.tkit.onecx.product.store.operator.product.ProductStatus;
import org.tkit.onecx.product.store.test.AbstractTest;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
diff --git a/src/test/java/org/tkit/onecx/product/store/operator/ProductControllerTest.java b/src/test/java/org/tkit/onecx/product/store/operator/ProductControllerTest.java
index 53b6db4..58a204a 100644
--- a/src/test/java/org/tkit/onecx/product/store/operator/ProductControllerTest.java
+++ b/src/test/java/org/tkit/onecx/product/store/operator/ProductControllerTest.java
@@ -17,6 +17,9 @@
import org.junit.jupiter.params.provider.MethodSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.tkit.onecx.product.store.operator.product.Product;
+import org.tkit.onecx.product.store.operator.product.ProductSpec;
+import org.tkit.onecx.product.store.operator.product.ProductStatus;
import org.tkit.onecx.product.store.test.AbstractTest;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
diff --git a/src/test/java/org/tkit/onecx/product/store/operator/SlotControllerResponseTest.java b/src/test/java/org/tkit/onecx/product/store/operator/SlotControllerResponseTest.java
new file mode 100644
index 0000000..6820820
--- /dev/null
+++ b/src/test/java/org/tkit/onecx/product/store/operator/SlotControllerResponseTest.java
@@ -0,0 +1,54 @@
+package org.tkit.onecx.product.store.operator;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+
+import jakarta.inject.Inject;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.tkit.onecx.product.store.operator.client.ProductStoreService;
+import org.tkit.onecx.product.store.operator.slot.Slot;
+import org.tkit.onecx.product.store.operator.slot.SlotController;
+import org.tkit.onecx.product.store.operator.slot.SlotSpec;
+import org.tkit.onecx.product.store.operator.slot.SlotStatus;
+import org.tkit.onecx.product.store.test.AbstractTest;
+
+import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+class SlotControllerResponseTest extends AbstractTest {
+
+ @InjectMock
+ ProductStoreService productStoreService;
+
+ @Inject
+ SlotController controller;
+
+ @BeforeEach
+ void beforeAll() {
+ Mockito.when(productStoreService.updateSlot(any())).thenReturn(404);
+ }
+
+ @Test
+ void testWrongResponse() throws Exception {
+
+ var s = new SlotSpec();
+ s.setProductName("product");
+ s.setAppId("m1");
+ s.setName("m1");
+
+ var m = new Slot();
+ m.setSpec(s);
+
+ UpdateControl result = controller.reconcile(m, null);
+ assertThat(result).isNotNull();
+ assertThat(result.getResource()).isNotNull();
+ assertThat(result.getResource().getStatus()).isNotNull();
+ assertThat(result.getResource().getStatus().getStatus()).isNotNull().isEqualTo(SlotStatus.Status.UNDEFINED);
+
+ }
+}
diff --git a/src/test/java/org/tkit/onecx/product/store/operator/SlotControllerTest.java b/src/test/java/org/tkit/onecx/product/store/operator/SlotControllerTest.java
new file mode 100644
index 0000000..aee60e1
--- /dev/null
+++ b/src/test/java/org/tkit/onecx/product/store/operator/SlotControllerTest.java
@@ -0,0 +1,157 @@
+package org.tkit.onecx.product.store.operator;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.awaitility.Awaitility.await;
+
+import java.util.stream.Stream;
+
+import jakarta.inject.Inject;
+
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.tkit.onecx.product.store.operator.slot.Slot;
+import org.tkit.onecx.product.store.operator.slot.SlotSpec;
+import org.tkit.onecx.product.store.operator.slot.SlotStatus;
+import org.tkit.onecx.product.store.test.AbstractTest;
+
+import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.javaoperatorsdk.operator.Operator;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+class SlotControllerTest extends AbstractTest {
+
+ static final Logger log = LoggerFactory.getLogger(SlotControllerTest.class);
+
+ @Inject
+ Operator operator;
+
+ @Inject
+ KubernetesClient client;
+
+ @BeforeAll
+ public static void init() {
+ Awaitility.setDefaultPollDelay(2, SECONDS);
+ Awaitility.setDefaultPollInterval(2, SECONDS);
+ Awaitility.setDefaultTimeout(10, SECONDS);
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTestData")
+ void SlotTest(String name, SlotSpec spec, SlotStatus.Status status) {
+
+ operator.start();
+
+ Slot slot = new Slot();
+ slot.setMetadata(new ObjectMetaBuilder().withName(name).withNamespace(client.getNamespace()).build());
+ slot.setSpec(spec);
+
+ client.resource(slot).serverSideApply();
+
+ await().pollDelay(2, SECONDS).untilAsserted(() -> {
+ SlotStatus mfeStatus = client.resource(slot).get().getStatus();
+ assertThat(mfeStatus).isNotNull();
+ assertThat(mfeStatus.getStatus()).isNotNull().isEqualTo(status);
+ });
+ }
+
+ private static Stream provideTestData() {
+ return Stream.of(
+ Arguments.of("test-1", createSpec("test-1", "product-test", "test1"), SlotStatus.Status.CREATED),
+ Arguments.of("test-2", createSpec("test-2", "product-test-2", "test2"), SlotStatus.Status.CREATED),
+ Arguments.of("test-3", createSpec("test-3", "product-test-2", "test3"), SlotStatus.Status.UPDATED),
+ Arguments.of("test-error-1", createSpec("test-error-1", "product-test-2", "test2"),
+ SlotStatus.Status.ERROR),
+ Arguments.of("test-error-2", createSpec("test-error-2", "product-test-2", "test2"),
+ SlotStatus.Status.ERROR));
+ }
+
+ private static SlotSpec createSpec(String appId, String productName, String name) {
+ SlotSpec spec = new SlotSpec();
+ spec.setAppId(appId);
+ spec.setProductName(productName);
+ spec.setName(name);
+ spec.setDescription("description");
+ return spec;
+ }
+
+ @Test
+ void SlotEmptySpecTest() {
+
+ operator.start();
+
+ Slot slot = new Slot();
+ slot.setMetadata(new ObjectMetaBuilder().withName("empty-spec").withNamespace(client.getNamespace()).build());
+ slot.setSpec(new SlotSpec());
+
+ client.resource(slot).serverSideApply();
+
+ await().pollDelay(2, SECONDS).untilAsserted(() -> {
+ SlotStatus mfeStatus = client.resource(slot).get().getStatus();
+ assertThat(mfeStatus).isNotNull();
+ assertThat(mfeStatus.getStatus()).isNotNull().isEqualTo(SlotStatus.Status.ERROR);
+ });
+ }
+
+ @Test
+ void SlotNullSpecTest() {
+
+ operator.start();
+
+ Slot slot = new Slot();
+ slot.setMetadata(new ObjectMetaBuilder().withName("null-spec").withNamespace(client.getNamespace()).build());
+ slot.setSpec(null);
+
+ client.resource(slot).serverSideApply();
+
+ await().pollDelay(4, SECONDS).untilAsserted(() -> {
+ SlotStatus mfeStatus = client.resource(slot).get().getStatus();
+ assertThat(mfeStatus).isNull();
+ });
+
+ }
+
+ @Test
+ void SlotUpdateEmptySpecTest() {
+
+ operator.start();
+
+ var m = new SlotSpec();
+ m.setAppId("test-1");
+ m.setProductName("product-test");
+
+ Slot slot = new Slot();
+ slot
+ .setMetadata(new ObjectMetaBuilder().withName("to-update-spec").withNamespace(client.getNamespace()).build());
+ slot.setSpec(m);
+
+ log.info("Creating test slot object: {}", slot);
+ client.resource(slot).serverSideApply();
+
+ await().pollDelay(2, SECONDS).untilAsserted(() -> {
+ SlotStatus mfeStatus = client.resource(slot).get().getStatus();
+ assertThat(mfeStatus).isNotNull();
+ assertThat(mfeStatus.getStatus()).isNotNull().isEqualTo(SlotStatus.Status.CREATED);
+ });
+
+ client.resource(slot).inNamespace(client.getNamespace())
+ .edit(s -> {
+ s.setSpec(null);
+ return s;
+ });
+
+ await().pollDelay(4, SECONDS).untilAsserted(() -> {
+ SlotStatus mfeStatus = client.resource(slot).get().getStatus();
+ assertThat(mfeStatus).isNotNull();
+ assertThat(mfeStatus.getStatus()).isNotNull().isEqualTo(SlotStatus.Status.CREATED);
+ });
+ }
+}
diff --git a/src/test/resources/mockserver/onecx-product-store-slot-mock.json b/src/test/resources/mockserver/onecx-product-store-slot-mock.json
new file mode 100644
index 0000000..424d021
--- /dev/null
+++ b/src/test/resources/mockserver/onecx-product-store-slot-mock.json
@@ -0,0 +1,52 @@
+[
+ {
+ "id": "6",
+ "httpRequest": {
+ "method" : "PUT",
+ "path": "/operator/slot/v1/product-test/test-1"
+ },
+ "httpResponse": {
+ "statusCode": 201
+ }
+ },
+ {
+ "id": "7",
+ "httpRequest": {
+ "method" : "PUT",
+ "path": "/operator/slot/v1/product-test-2/test-2"
+ },
+ "httpResponse": {
+ "statusCode": 201
+ }
+ },
+ {
+ "id": "8",
+ "httpRequest": {
+ "method" : "PUT",
+ "path": "/operator/slot/v1/product-test-2/test-3"
+ },
+ "httpResponse": {
+ "statusCode": 200
+ }
+ },
+ {
+ "id": "9",
+ "httpRequest": {
+ "method" : "PUT",
+ "path": "/operator/slot/v1/product-test-2/test-error-1"
+ },
+ "httpResponse": {
+ "statusCode": 500
+ }
+ },
+ {
+ "id": "10",
+ "httpRequest": {
+ "method" : "PUT",
+ "path": "/operator/slot/v1/product-test-2/test-error-2"
+ },
+ "httpResponse": {
+ "statusCode": 400
+ }
+ }
+]
\ No newline at end of file