diff --git a/.github/workflows/build-and-deploy.yaml b/.github/workflows/build-and-deploy.yaml index 3ad2a92..2e9d9be 100644 --- a/.github/workflows/build-and-deploy.yaml +++ b/.github/workflows/build-and-deploy.yaml @@ -81,60 +81,60 @@ jobs: namespace: operators action: deploy - ## API cluster ## - - name: Bake api manifests with Kustomize - uses: azure/k8s-bake@v2 - with: - renderEngine: 'kustomize' - kustomizationPath: 'kustomize/overlays/api' - id: bake-api - - - uses: azure/login@v1 - with: - creds: '${{ secrets.AKS_API_FINT_GITHUB }}' - - - name: Set the target Api cluster. - uses: azure/aks-set-context@v3 - with: - cluster-name: ${{ env.API_CLUSTER_NAME }} - resource-group: ${{ env.API_CLUSTER_RESOURCE_GROUP }} - admin: 'true' - use-kubelogin: 'true' - - - name: Deploy to Api - uses: azure/k8s-deploy@v4.9 - with: - manifests: ${{ steps.bake-api.outputs.manifestsBundle }} - images: | - ${{ steps.meta.outputs.tags }} - namespace: operators - action: deploy - - ## Beta cluster ## - - name: Bake beta manifests with Kustomize - uses: azure/k8s-bake@v2 - with: - renderEngine: 'kustomize' - kustomizationPath: 'kustomize/overlays/beta' - id: bake-beta - - - uses: azure/login@v1 - with: - creds: '${{ secrets.AKS_BETA_FINT_GITHUB }}' - - - name: Set the target Beta cluster. - uses: azure/aks-set-context@v3 - with: - cluster-name: ${{ env.BETA_CLUSTER_NAME }} - resource-group: ${{ env.BETA_CLUSTER_RESOURCE_GROUP }} - admin: 'true' - use-kubelogin: 'true' - - - name: Deploy to Beta - uses: azure/k8s-deploy@v4.9 - with: - manifests: ${{ steps.bake-beta.outputs.manifestsBundle }} - images: | - ${{ steps.meta.outputs.tags }} - namespace: operators - action: deploy \ No newline at end of file +# ## API cluster ## +# - name: Bake api manifests with Kustomize +# uses: azure/k8s-bake@v2 +# with: +# renderEngine: 'kustomize' +# kustomizationPath: 'kustomize/overlays/api' +# id: bake-api +# +# - uses: azure/login@v1 +# with: +# creds: '${{ secrets.AKS_API_FINT_GITHUB }}' +# +# - name: Set the target Api cluster. +# uses: azure/aks-set-context@v3 +# with: +# cluster-name: ${{ env.API_CLUSTER_NAME }} +# resource-group: ${{ env.API_CLUSTER_RESOURCE_GROUP }} +# admin: 'true' +# use-kubelogin: 'true' +# +# - name: Deploy to Api +# uses: azure/k8s-deploy@v4.9 +# with: +# manifests: ${{ steps.bake-api.outputs.manifestsBundle }} +# images: | +# ${{ steps.meta.outputs.tags }} +# namespace: operators +# action: deploy +# +# ## Beta cluster ## +# - name: Bake beta manifests with Kustomize +# uses: azure/k8s-bake@v2 +# with: +# renderEngine: 'kustomize' +# kustomizationPath: 'kustomize/overlays/beta' +# id: bake-beta +# +# - uses: azure/login@v1 +# with: +# creds: '${{ secrets.AKS_BETA_FINT_GITHUB }}' +# +# - name: Set the target Beta cluster. +# uses: azure/aks-set-context@v3 +# with: +# cluster-name: ${{ env.BETA_CLUSTER_NAME }} +# resource-group: ${{ env.BETA_CLUSTER_RESOURCE_GROUP }} +# admin: 'true' +# use-kubelogin: 'true' +# +# - name: Deploy to Beta +# uses: azure/k8s-deploy@v4.9 +# with: +# manifests: ${{ steps.bake-beta.outputs.manifestsBundle }} +# images: | +# ${{ steps.meta.outputs.tags }} +# namespace: operators +# action: deploy diff --git a/README.md b/README.md index e858cc4..7add453 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,44 @@ openssl genpkey -algorithm rsa ``` - +### Client .yaml example file: ```yaml apiVersion: fintlabs.no/v1alpha1 kind: FintClient metadata: - name: frodes-test-client + name: flais-test-client + namespace: fintlabs-no + labels: + app.kubernetes.io/name: test-client + app.kubernetes.io/instance: test-service-backend-client_rogfk_no + app.kubernetes.io/version: latest + app.kubernetes.io/component: backend + app.kubernetes.io/part-of: arkiv + fintlabs.no/team: flais + fintlabs.no/org-id: fintlabs.no + +spec: + orgId: fintlabs.no + note: Dette er en test. Nu kjør vi!! + components: + - administrasjon_personal + - utdanning_elev +``` + +### Adapter +.yaml example file: +```yaml +apiVersion: fintlabs.no/v1alpha1 +kind: FintAdapter +metadata: + name: flais-test-adapter namespace: fintlabs-no labels: app.kubernetes.io/name: test-adapter app.kubernetes.io/instance: test-adapter_rogfk_no app.kubernetes.io/version: latest - app.kubernetes.io/component: adapter + app.kubernetes.io/component: arkiv-adapter app.kubernetes.io/part-of: arkiv fintlabs.no/team: flais fintlabs.no/org-id: fintlabs.no @@ -33,4 +58,7 @@ spec: components: - administrasjon_personal - utdanning_elev + assetIds: + - test.fylke.no + - test.annet_fylke.no ``` diff --git a/build.gradle b/build.gradle index 867b409..8bacb29 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'no.fintlabs:flais-operator-starter:1.0.0' - annotationProcessor 'io.fabric8:crd-generator-apt:6.2.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' + testImplementation 'org.testng:testng:7.1.0' + testImplementation 'junit:junit:4.13.1' + annotationProcessor 'io.fabric8:crd-generator-apt:6.2.0' implementation 'org.springframework.kafka:spring-kafka' implementation 'no.fintlabs:fint-kafka:4.0.1' diff --git a/examples/fint-adapter.yaml b/examples/fint-adapter.yaml index a41dcbd..f27ab7b 100644 --- a/examples/fint-adapter.yaml +++ b/examples/fint-adapter.yaml @@ -1,7 +1,7 @@ apiVersion: fintlabs.no/v1alpha1 kind: FintAdapter metadata: - name: sondres-test-adapter + name: test-adapter namespace: fintlabs-no labels: app.kubernetes.io/name: test-adapter @@ -18,4 +18,8 @@ spec: components: - administrasjon_personal - utdanning_elev + assetIds: + - fylkesting_viken_no + - fylkesting_rogfk_no + - fylkesting_flais_no diff --git a/examples/fint-adapter2.yaml b/examples/fint-adapter2.yaml index 410abad..845ad37 100644 --- a/examples/fint-adapter2.yaml +++ b/examples/fint-adapter2.yaml @@ -1,7 +1,7 @@ apiVersion: fintlabs.no/v1alpha1 kind: FintAdapter metadata: - name: test-adapter + name: testadapter namespace: fintlabs-no labels: app.kubernetes.io/name: test-adapter @@ -18,9 +18,10 @@ spec: components: - administrasjon_personal - utdanning_elev - assets: - - fylkesting.fintlabs.no + assetIds: + - fylkesting.viken.no + - fylkesting.telemark.no # - frad.fintlabs.no # assetIds: "org,asset,asset" -# fintAdapterIDs: "ou=viken_no,ou=assets,ou=viken_no,ou=organisations,o=fint" \ No newline at end of file +# fintAdapterIDs: "ou=viken_no,ou=assets,ou=viken_no,ou=organisations,o=fint" diff --git a/examples/fint-client.yaml b/examples/fint-client.yaml index e200f42..60879b6 100644 --- a/examples/fint-client.yaml +++ b/examples/fint-client.yaml @@ -1,7 +1,7 @@ apiVersion: fintlabs.no/v1alpha1 kind: FintClient metadata: - name: frodes-test-client + name: flais-test-client namespace: fintlabs-no labels: app.kubernetes.io/name: test-adapter diff --git a/kustomize/base/fintadapters.fintlabs.no-v1.yml b/kustomize/base/fintadapters.fintlabs.no-v1.yml index 2c634ed..0fe1cdf 100644 --- a/kustomize/base/fintadapters.fintlabs.no-v1.yml +++ b/kustomize/base/fintadapters.fintlabs.no-v1.yml @@ -25,6 +25,10 @@ spec: items: type: string type: array + assetIds: + items: + type: string + type: array type: object status: properties: diff --git a/kustomize/overlays/alpha/exclude-kafka.yaml b/kustomize/overlays/alpha/exclude-kafka.yaml new file mode 100644 index 0000000..fa5ca86 --- /dev/null +++ b/kustomize/overlays/alpha/exclude-kafka.yaml @@ -0,0 +1,9 @@ +$patch: delete +apiVersion: "fintlabs.no/v1alpha1" +kind: KafkaUserAndAcl +metadata: + name: finterator +spec: + acls: + - permission: admin + topic: 'flais-io.fint-customer-objects.*' \ No newline at end of file diff --git a/kustomize/overlays/alpha/kustomization.yaml b/kustomize/overlays/alpha/kustomization.yaml index bed81a1..95c5341 100644 --- a/kustomize/overlays/alpha/kustomization.yaml +++ b/kustomize/overlays/alpha/kustomization.yaml @@ -2,6 +2,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../../base +patchesStrategicMerge: + - exclude-kafka.yaml patches: - patch: |- - op: replace @@ -9,4 +11,13 @@ patches: value: "vaults/aks-alpha-vault/items/finterator" target: kind: OnePasswordItem + name: finterator + - patch: |- + - op: add + path: "/spec/template/spec/containers/0/env/-" + value: + name: fint.application-id + value: finterator-alpha + target: + kind: Deployment name: finterator \ No newline at end of file diff --git a/src/main/java/no/fintlabs/CrdUtilities.java b/src/main/java/no/fintlabs/CrdUtilities.java index 075d322..fe39e66 100644 --- a/src/main/java/no/fintlabs/CrdUtilities.java +++ b/src/main/java/no/fintlabs/CrdUtilities.java @@ -1,12 +1,14 @@ package no.fintlabs; import io.fabric8.kubernetes.api.model.HasMetadata; +import org.springframework.util.StringUtils; import java.util.Optional; public class CrdUtilities { public static Optional getValueFromAnnotationByKey(HasMetadata crd, String key) { - return Optional.ofNullable(crd.getMetadata().getAnnotations().get(key)); + return Optional.ofNullable(crd.getMetadata().getAnnotations().get(key)) + .filter(StringUtils::hasText); } } \ No newline at end of file diff --git a/src/main/java/no/fintlabs/LdapNameGeneratorUtil.java b/src/main/java/no/fintlabs/LdapNameGeneratorUtil.java new file mode 100644 index 0000000..bed8de4 --- /dev/null +++ b/src/main/java/no/fintlabs/LdapNameGeneratorUtil.java @@ -0,0 +1,70 @@ +package no.fintlabs; + +import org.apache.commons.lang3.RandomStringUtils; + +public class LdapNameGeneratorUtil { + + public static final int RANDOM_CHARS = 5; + public static final int MAX_LDAP_CHARS = 64; + + public static String generate(String crdName, String orgId, String type) { + + // drosjeloyve-beta-trondelagfylke-no-wayzk@client.trondelagfylke.no + // drosjeloyve-beta-trondelagfylke-wayzk@client.trondelagfylke.no + // drosjeloyve-wayzk@client.trondelagfylke.no + + String randomString = RandomStringUtils.randomAlphabetic(RANDOM_CHARS).toLowerCase(); + + // Remove if too long and contains -no- + if (getFullNameLength(crdName, randomString, orgId, type) > MAX_LDAP_CHARS && crdName.contains("-no-")) { + crdName = crdName.replace("-no-", "-"); + } + + // remove if too long and ends with -no + if (getFullNameLength(crdName, randomString, orgId, type) > MAX_LDAP_CHARS && crdName.endsWith("-no")) { + crdName = crdName.substring(0, crdName.length() - 3); + } + + // remove if too long and contains fylke + if (getFullNameLength(crdName, randomString, orgId, type) > MAX_LDAP_CHARS && crdName.contains("fylke")) { + crdName = crdName.replace("fylke", ""); + } + + while (getFullNameLength(crdName, randomString, orgId, type) > MAX_LDAP_CHARS) { + crdName = removeCharacterFromLongestPart(crdName); + } + + return String.format("%s-%s", crdName, randomString); + } + + private static String removeCharacterFromLongestPart(String crdName) { + String[] parts = crdName.split("-"); + int longestPartIndex = -1; + int longestPartLength = 0; + + // Finn indeksen til den lengste delen, sortert fra høyre + for (int i = 0; i < parts.length; i++) { + if (parts[i].length() >= longestPartLength) { + longestPartLength = parts[i].length(); + longestPartIndex = i; + } + } + + // Fjern siste tegn fra den lengste delen + if (longestPartIndex != -1 && parts[longestPartIndex].length() > 0) { + parts[longestPartIndex] = parts[longestPartIndex].substring(0, parts[longestPartIndex].length() - 1); + } + + // Sett sammen strengen igjen + return String.join("-", parts); + } + + public static int getFullNameLength(String crdName, String randomString, String orgId, String type) { + return generateFullName(crdName, randomString, orgId, type).length(); + } + + public static String generateFullName(String crdName, String randomString, String orgId, String type) { + return String.format("%s-%s@%s.%s", crdName, randomString, type, orgId); + } + +} diff --git a/src/main/java/no/fintlabs/adapter/Adapter.java b/src/main/java/no/fintlabs/adapter/Adapter.java index 4ba2a74..4b7c93b 100644 --- a/src/main/java/no/fintlabs/adapter/Adapter.java +++ b/src/main/java/no/fintlabs/adapter/Adapter.java @@ -60,6 +60,26 @@ public final class Adapter implements Serializable { @Builder.Default private List accessPackages = new ArrayList<>(); + public void addAssets(String assetDn) { + if (assets.stream().noneMatch(assetDn::equalsIgnoreCase)) { + assets.add(assetDn); + } + } + + public void removeAssets(String assetDn) { + assets.removeIf(assetId -> assetId.equalsIgnoreCase(assetDn)); + } + + public void addAssetId(String assetIdDn) { + if (assetIds.stream().noneMatch(assetIdDn::equalsIgnoreCase)) { + assetIds.add(assetIdDn); + } + } + + public void removeAssetId(String assetIdDn) { + assetIds.removeIf(assetId -> assetId.equalsIgnoreCase(assetIdDn)); + } + public void addComponent(String componentDn) { if (components.stream().noneMatch(componentDn::equalsIgnoreCase)) { components.add(componentDn); diff --git a/src/main/java/no/fintlabs/adapter/FintAdapterDependentResource.java b/src/main/java/no/fintlabs/adapter/FintAdapterDependentResource.java index 5f3432a..e434dd2 100644 --- a/src/main/java/no/fintlabs/adapter/FintAdapterDependentResource.java +++ b/src/main/java/no/fintlabs/adapter/FintAdapterDependentResource.java @@ -6,11 +6,9 @@ import io.javaoperatorsdk.operator.processing.dependent.Updater; import lombok.extern.slf4j.Slf4j; import no.fintlabs.FlaisExternalDependentResource; +import no.fintlabs.LdapNameGeneratorUtil; import no.fintlabs.SecretService; -import no.fintlabs.client.Client; -import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.SerializationUtils; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -60,7 +58,7 @@ public Adapter desired(FintAdapterCrd primary, Context context) private Supplier handleDesiredOnNew(FintAdapterCrd primary) { return () -> { - String adapterName = String.format("%s-%s", primary.getMetadata().getName(), RandomStringUtils.randomAlphabetic(5).toLowerCase()); + String adapterName = LdapNameGeneratorUtil.generate(primary.getMetadata().getName(), primary.getSpec().getOrgId(), "adapter"); Adapter adapter = Adapter .builder() .name(adapterName) @@ -71,6 +69,8 @@ private Supplier handleDesiredOnNew(FintAdapterCrd primary) { .build(); primary.getSpec().getComponents() .forEach(component -> adapter.addComponent(String.format("ou=%s,ou=components,o=fint", component))); + primary.getSpec().getAssetIds() + .forEach(adapter::addAssetId); log.info("No adapter found in event store. Desired adapter is: {}", adapter); return adapter; @@ -88,9 +88,12 @@ private Function handleDesiredForExisting(FintAdapterCrd prima Adapter desiredAdapter = SerializationUtils.clone(currentAdapter); desiredAdapter.setNote(generateNote(primary)); desiredAdapter.getComponents().clear(); + desiredAdapter.getAssetIds().clear(); desiredAdapter.setManaged(true); primary.getSpec().getComponents() .forEach(component -> desiredAdapter.addComponent(String.format("ou=%s,ou=components,o=fint", component))); + primary.getSpec().getAssetIds() + .forEach(desiredAdapter::addAssetId); return desiredAdapter; }; @@ -122,29 +125,20 @@ public Adapter create(Adapter desired, FintAdapterCrd primary, Context fetchResources(FintAdapterCrd primaryResource) { - Set adapters = fintAdapterRepository.get(primaryResource); - - for (var adapter : adapters) { - if (isSecretOrPasswordMissing(adapter)) { - adapter.setNote("Trigger update because clientSecret or password is empty"); - log.info("Change adapter '{}' to trigger update", adapter.getName()); - } - } - - return adapters; - } - - private boolean isSecretOrPasswordMissing(Adapter adapter) { - return /*adapter.isManaged() &&*/ (StringUtils.isEmpty(adapter.getClientSecret()) || StringUtils.isEmpty(adapter.getPassword())); + return fintAdapterRepository.get(primaryResource); } @Override public Matcher.Result match( Adapter actualResource, FintAdapterCrd primary, Context context ) { - DesiredEqualsMatcher matcher = new DesiredEqualsMatcher<>(this); + // TODO: 27/10/2023 Finn ut hvorfor managed blir false + actualResource.setManaged(true); - return matcher.match(actualResource, primary, context); + DesiredEqualsMatcher matcher = new DesiredEqualsMatcher<>(this); + Matcher.Result result = matcher.match(actualResource, primary, context); + log.debug("Match {} ={}", primary.getMetadata().getName(), result.matched()); + return result; } } diff --git a/src/main/java/no/fintlabs/adapter/FintAdapterReconciler.java b/src/main/java/no/fintlabs/adapter/FintAdapterReconciler.java index 3dacd4b..ad3a9cb 100644 --- a/src/main/java/no/fintlabs/adapter/FintAdapterReconciler.java +++ b/src/main/java/no/fintlabs/adapter/FintAdapterReconciler.java @@ -7,7 +7,9 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import lombok.extern.slf4j.Slf4j; import no.fintlabs.FlaisReconiler; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; import java.util.List; @@ -19,12 +21,30 @@ public FintAdapterReconciler( FintAdapterWorkflow workflow, List> eventSourceProviders, List> deleters - ) { + ) { super(workflow, eventSourceProviders, deleters); } + @Value("${fint.accepted.adapter-name:}") + private String acceptedName; + + @Value("${fint.accepted.adapter-namespace:}") + private String acceptedNamespace; + @Override public UpdateControl reconcile(FintAdapterCrd resource, Context context) { - return super.reconcile(resource, context); + //return super.reconcile(resource, context); + + String name = resource.getMetadata().getName(); + String namespace = resource.getMetadata().getNamespace(); + + if (name.contains(acceptedName) && namespace.contains(acceptedNamespace) + && StringUtils.hasText(acceptedName) && StringUtils.hasText(acceptedNamespace)) { + log.info("Include update for " + name + " in " + namespace); + return super.reconcile(resource, context); + } else { + log.info("Skip update for " + name + " in " + namespace); + return UpdateControl.noUpdate(); + } } } diff --git a/src/main/java/no/fintlabs/adapter/FintAdapterRepository.java b/src/main/java/no/fintlabs/adapter/FintAdapterRepository.java index 5e2ea46..091b581 100644 --- a/src/main/java/no/fintlabs/adapter/FintAdapterRepository.java +++ b/src/main/java/no/fintlabs/adapter/FintAdapterRepository.java @@ -83,18 +83,20 @@ public Set get(FintAdapterCrd crd) { Optional responseOptional = adapterEventRequestProducerService.get(createRequestEvent(crd, dn.get())); if (responseOptional.isEmpty()) { + log.error("Empty response from Kafka. The request has probably timed out. Client: {}", dn.get()); throw new CustomerObjectResponseException("Empty response from Kafka. The request has probably timed out. Client: " + dn.get()); } AdapterEvent response = responseOptional.get(); if (response.hasError()){ + log.error("Error response from Kafka: {}", response.getErrorMessage()); throw new CustomerObjectResponseException(response.getErrorMessage()); } if (response.getObject() == null) { - log.debug("Object in response is null"); - return Collections.emptySet(); + log.error("DN has been set, but the client could not be found! {}", dn.get()); + throw new IllegalStateException("DN has been set, but the client could not be found! " + dn.get()); } return Collections.singleton(response.getObject()); diff --git a/src/main/java/no/fintlabs/adapter/FintAdapterSpec.java b/src/main/java/no/fintlabs/adapter/FintAdapterSpec.java index bb825ff..b0bfeaf 100644 --- a/src/main/java/no/fintlabs/adapter/FintAdapterSpec.java +++ b/src/main/java/no/fintlabs/adapter/FintAdapterSpec.java @@ -16,5 +16,6 @@ public class FintAdapterSpec implements FlaisSpec { private String orgId; private String note; private List components = Collections.emptyList(); + private List assetIds = Collections.emptyList(); } diff --git a/src/main/java/no/fintlabs/client/FintClientDependentResource.java b/src/main/java/no/fintlabs/client/FintClientDependentResource.java index 4ec7333..88a5c04 100644 --- a/src/main/java/no/fintlabs/client/FintClientDependentResource.java +++ b/src/main/java/no/fintlabs/client/FintClientDependentResource.java @@ -5,7 +5,9 @@ import io.javaoperatorsdk.operator.processing.dependent.Matcher; import io.javaoperatorsdk.operator.processing.dependent.Updater; import lombok.extern.slf4j.Slf4j; +import no.fintlabs.CustomerObjectResponseException; import no.fintlabs.FlaisExternalDependentResource; +import no.fintlabs.LdapNameGeneratorUtil; import no.fintlabs.SecretService; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.SerializationUtils; @@ -60,7 +62,8 @@ protected Client desired(FintClientCrd primary, Context context) private Supplier handleDesiredOnNew(FintClientCrd primary) { return () -> { - String clientName = String.format("%s-%s", primary.getMetadata().getName(), RandomStringUtils.randomAlphabetic(5).toLowerCase()); + String clientName = LdapNameGeneratorUtil.generate(primary.getMetadata().getName(), primary.getSpec().getOrgId(), "client"); + Client client = Client .builder() .name(clientName) @@ -124,26 +127,18 @@ public Client create(Client desired, FintClientCrd primary, Context fetchResources(FintClientCrd primaryResource) { - Set clients = fintClientRepository.get(primaryResource); - - for (var client : clients) { - if (isSecretOrPasswordMissing(client)) { - client.setNote("Trigger update because clientSecret or password is empty"); - log.info("Change client '{}' to trigger update", client.getName()); - } - } - - return clients; - } - - private boolean isSecretOrPasswordMissing(Client client) { - return /*client.isManaged() &&*/ (StringUtils.isEmpty(client.getClientSecret()) || StringUtils.isEmpty(client.getPassword())); + return fintClientRepository.get(primaryResource); } @Override public Matcher.Result match(Client actualResource, FintClientCrd primary, Context context) { + // TODO: 27/10/2023 Finn ut hvorfor managed blir false + actualResource.setManaged(true); + DesiredEqualsMatcher matcher = new DesiredEqualsMatcher<>(this); - return matcher.match(actualResource, primary, context); + Matcher.Result result = matcher.match(actualResource, primary, context); + log.debug("Match {} ={}", primary.getMetadata().getName(), result.matched()); + return result; } } \ No newline at end of file diff --git a/src/main/java/no/fintlabs/client/FintClientReconiler.java b/src/main/java/no/fintlabs/client/FintClientReconiler.java index f714d08..049835f 100644 --- a/src/main/java/no/fintlabs/client/FintClientReconiler.java +++ b/src/main/java/no/fintlabs/client/FintClientReconiler.java @@ -7,7 +7,9 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import lombok.extern.slf4j.Slf4j; import no.fintlabs.FlaisReconiler; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; import java.util.List; @@ -21,10 +23,26 @@ public FintClientReconiler(FintClientWorkflow workflow, super(workflow, eventSourceProviders, deleters); } + @Value("${fint.accepted.client-name:}") + private String acceptedName; + + @Value("${fint.accepted.client-namespace:}") + private String acceptedNamespace; + @Override public UpdateControl reconcile(FintClientCrd resource, Context context) { - return super.reconcile(resource, context); - } + //return super.reconcile(resource, context); + String name = resource.getMetadata().getName(); + String namespace = resource.getMetadata().getNamespace(); + if (name.contains(acceptedName) && namespace.contains(acceptedNamespace) + && StringUtils.hasText(acceptedName) && StringUtils.hasText(acceptedNamespace)) { + log.info("Include update for " + name + " in " + namespace); + return super.reconcile(resource, context); + } else { + log.info("Skip update for " + name + " in " + namespace); + return UpdateControl.noUpdate(); + } + } } diff --git a/src/main/java/no/fintlabs/client/FintClientRepository.java b/src/main/java/no/fintlabs/client/FintClientRepository.java index b6a1276..08957f5 100644 --- a/src/main/java/no/fintlabs/client/FintClientRepository.java +++ b/src/main/java/no/fintlabs/client/FintClientRepository.java @@ -74,7 +74,7 @@ public Set get(FintClientCrd crd) { Optional dn = getValueFromAnnotationByKey(crd, FintClientDependentResource.ANNOTATION_CLIENT_DN); if (dn.isEmpty()) { - log.debug("Skipping client lookup due to missing DN in CRD."); + log.warn("Skipping client lookup due to missing DN in CRD."); return Collections.emptySet(); } @@ -82,18 +82,20 @@ public Set get(FintClientCrd crd) { Optional responseOptional = clientEventRequestProducerService.get(createRequestEvent(crd, dn.get())); if (responseOptional.isEmpty()) { + log.error("Empty response from Kafka. The request has probably timed out. Client: {}", dn.get()); throw new CustomerObjectResponseException("Empty response from Kafka. The request has probably timed out. Client: " + dn.get()); } ClientEvent response = responseOptional.get(); if (response.hasError()) { + log.error("Error response from Kafka: {}", response.getErrorMessage()); throw new CustomerObjectResponseException(response.getErrorMessage()); } if (response.getObject() == null) { - log.debug("Object in response is null"); - return Collections.emptySet(); + log.error("DN has been set, but the client could not be found! {}", dn.get()); + throw new IllegalStateException("DN has been set, but the client could not be found! " + dn.get()); } return Collections.singleton(response.getObject()); diff --git a/src/main/java/no/fintlabs/client/FintClientSecretDependentResource.java b/src/main/java/no/fintlabs/client/FintClientSecretDependentResource.java index a1867dd..de782bf 100644 --- a/src/main/java/no/fintlabs/client/FintClientSecretDependentResource.java +++ b/src/main/java/no/fintlabs/client/FintClientSecretDependentResource.java @@ -4,7 +4,11 @@ import io.fabric8.kubernetes.api.model.SecretBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.Matcher; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; +import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; import lombok.extern.slf4j.Slf4j; import no.fintlabs.FlaisKubernetesDependentResource; import no.fintlabs.SecretService; @@ -39,11 +43,9 @@ public FintClientSecretDependentResource(FintClientWorkflow workflow, protected Secret desired(FintClientCrd resource, Context context) { log.debug("Desired secret for {}", resource.getMetadata().getName()); - Client fintClient = context.getSecondaryResource(Client.class).orElseThrow(); HashMap labels = new HashMap<>(resource.getMetadata().getLabels()); - labels.put("app.kubernetes.io/managed-by", "finterator"); return new SecretBuilder() @@ -58,12 +60,9 @@ protected Secret desired(FintClientCrd resource, Context context) .addToData("fint.core.oauth2.client-id", encode((fintClient.getClientId()))) .addToData("fint.core.oauth2.client-secret", encode(secretService.decrypt(fintClient.getClientSecret()))) .build(); - - } public String encode(String value) { - if (value == null) value = ""; return Base64.getEncoder().encodeToString(value.getBytes()); } diff --git a/src/test/java/no/fintlabs/LdapNameGeneratorUtilTest.java b/src/test/java/no/fintlabs/LdapNameGeneratorUtilTest.java new file mode 100644 index 0000000..d02dc77 --- /dev/null +++ b/src/test/java/no/fintlabs/LdapNameGeneratorUtilTest.java @@ -0,0 +1,131 @@ +package no.fintlabs; + +import groovy.util.logging.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.util.Assert; + +import static org.junit.jupiter.api.Assertions.*; + +class LdapNameGeneratorUtilTest { + + @Test + void generateNormal() { + // Sett opp testdata + String crdName = "drosjeloyve"; + String orgId = "agderfk.no"; + String type = "client"; + + // Utfør metoden som skal testes + String result = LdapNameGeneratorUtil.generate(crdName, orgId, type); + + // Sjekk at resultatet er som forventet + assertEquals( "drosjeloyve", result.substring(0, result.length() - 6)); + System.out.println(result); + } + + @Test + void generateRemovalOfNo() { + // Sett opp testdata + String crdName = "drosjeloyve-beta-trondelagfylke-no"; + String orgId = "trondelagfylke.no"; + String type = "client"; + + // Utfør metoden som skal testes + String result = LdapNameGeneratorUtil.generate(crdName, orgId, type); + + // Sjekk at resultatet er som forventet + assertEquals( "drosjeloyve-beta-trondelagfylke", result.substring(0, result.length() - 6)); + System.out.println(result); + } + + @Test + void generateRemovalOfFylke() { + // Sett opp testdata + String crdName = "drosjeloyve-beta-test-trondelagfylke-no"; + String orgId = "trondelagfylke.no"; + String type = "client"; + + // Utfør metoden som skal testes + String result = LdapNameGeneratorUtil.generate(crdName, orgId, type); + + // Sjekk at resultatet er som forventet + assertEquals( "drosjeloyve-beta-test-trondelag", result.substring(0, result.length() - 6)); + System.out.println(result); + } + + @Test + void generateRemovalOfLongestPart() { + // Sett opp testdata + String crdName = "drosjeloyve-beta-trondelag-test1-t"; + String orgId = "trondelagfylke.no"; + String type = "client"; + + // Utfør metoden som skal testes + String result = LdapNameGeneratorUtil.generate(crdName, orgId, type); + + // Sjekk at resultatet er som forventet + assertEquals( "drosjeloyv-beta-trondelag-test1-t", result.substring(0, result.length() - 6)); + System.out.println(result); + } + + @Test + void generateRemovalOfLongestPart2() { + // Sett opp testdata + String crdName = "drosjeloyve-beta-trondelag-test1-tes"; + String orgId = "trondelagfylke.no"; + String type = "client"; + + // Utfør metoden som skal testes + String result = LdapNameGeneratorUtil.generate(crdName, orgId, type); + + // Sjekk at resultatet er som forventet + assertEquals( "drosjeloy-beta-trondela-test1-tes", result.substring(0, result.length() - 6)); + System.out.println(result); + } + + @Test + void generateRemovalOfLongestPart3() { + // Sett opp testdata + String crdName = "drosjeloyve-beta-trondelag-test1-tes-test"; + String orgId = "trondelagfylke.no"; + String type = "client"; + + // Utfør metoden som skal testes + String result = LdapNameGeneratorUtil.generate(crdName, orgId, type); + + // Sjekk at resultatet er som forventet + assertEquals( "drosje-beta-tronde-test1-tes-test", result.substring(0, result.length() - 6)); + System.out.println(result); + } + +// @Test +// void generateLong() { +// // Sett opp testdata +// String crdName = "drosjeloyve-beta-trondelagfylke-no"; +// String randomString = "wayzk"; +// String orgId = "trondelagfylke.no"; +// String type = "client"; +// +// // Utfør metoden som skal testes +// String result = LdapNameGeneratorUtil.generate(crdName, randomString, orgId, type); +// +// // Sjekk at resultatet er som forventet +// assertEquals("drosjeloyve-beta-trondelagfylke-no-wayzk@client.trondelagfylke.no", result); +// } + + @Test + void generateFullName() { + // Sett opp testdata + String crdName = "drosjeloyve-beta-trondelagfylke-no"; + String randomString = "wayzk"; + String orgId = "trondelagfylke.no"; + String type = "client"; + + // Utfør metoden som skal testes + String result = LdapNameGeneratorUtil.generateFullName(crdName, randomString, orgId, type); + + // Sjekk at resultatet er som forventet + assertEquals("drosjeloyve-beta-trondelagfylke-no-wayzk@client.trondelagfylke.no", result); + + } +} \ No newline at end of file diff --git a/src/test/java/no/fintlabs/adapter/AdapterTest.java b/src/test/java/no/fintlabs/adapter/AdapterTest.java new file mode 100644 index 0000000..33f065a --- /dev/null +++ b/src/test/java/no/fintlabs/adapter/AdapterTest.java @@ -0,0 +1,72 @@ +package no.fintlabs.adapter; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class AdapterTest { + // Test that assetId is added if it is not in the list + @Test + public void testAddAssetIdNotInList() { + Adapter adapter = testAdapter(); + adapter.addAssetId("asset1"); + assertTrue(adapter.getAssetIds().contains("asset1")); + } + + // Test that assetId is not added if it is already in the list + @Test + public void testAddAssetIdAlreadyInList() { + Adapter adapter = testAdapter(); + adapter.addAssetId("asset1"); + adapter.addAssetId("asset1"); + assertEquals(1,adapter.getAssetIds().size()); + } + + // Test that component is added if it is not in the list + @Test + public void testAddComponentWhenListIsEmpty() { + Adapter adapter = testAdapter(); + adapter.addComponent("component1"); + assertTrue(adapter.getComponents().contains("component1")); + assertEquals(1,adapter.getComponents().size()); + } + + // Test that component is added if it does not exist + @Test + public void testAddComponentWhenComponentDoesNotExist() { + Adapter adapter = testAdapter(); + adapter.addComponent("component1"); + assertEquals(1,adapter.getComponents().size()); + assertEquals("component1",adapter.getComponents().get(0)); + } + + // Test that component is not added if it already exists + @Test + public void testAddComponentWhenComponentExists() { + Adapter adapter = testAdapter(); + adapter.addComponent("component1"); + adapter.addComponent("component1"); + + assertEquals(1,adapter.getComponents().size()); + } + + private static Adapter testAdapter() { + return new Adapter( + "", + "", + false, + "", + new ArrayList<>(), + new ArrayList<>(), + "", + "", + "", + "", + "", + new ArrayList<>(), + new ArrayList<>()); + } +} diff --git a/src/test/java/no/fintlabs/adapter/FintAdapterDependentResourceTest.java b/src/test/java/no/fintlabs/adapter/FintAdapterDependentResourceTest.java new file mode 100644 index 0000000..7dc671a --- /dev/null +++ b/src/test/java/no/fintlabs/adapter/FintAdapterDependentResourceTest.java @@ -0,0 +1,94 @@ +package no.fintlabs.adapter; + +import io.fabric8.kubernetes.api.model.ObjectMeta; +import no.fintlabs.SecretService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +public class FintAdapterDependentResourceTest { + + @Test + public void testHandleDesiredForExisting() { + FintAdapterCrd primary = new FintAdapterCrd(); + Adapter currentAdapter = currentAdapter(); + Adapter desiredAdapter = desiredAdapter(); + primary.setMetadata(new ObjectMeta()); + System.out.println("Primary: " + primary); + + try { + FintAdapterDependentResource fintAdapterDependentResource = new FintAdapterDependentResource(new FintAdapterWorkflow(), mock(FintAdapterRepository.class), new SecretService()); + Method privateMethod = FintAdapterDependentResource.class.getDeclaredMethod("handleDesiredForExisting", FintAdapterCrd.class); + privateMethod.setAccessible(true); + Object function = privateMethod.invoke(fintAdapterDependentResource, primary); + Function handleDesiredForExisting = (Function) function; + desiredAdapter = handleDesiredForExisting.apply(currentAdapter); + Assertions.assertNotEquals(currentAdapter, desiredAdapter); + Assertions.assertTrue(desiredAdapter.getComponents().isEmpty()); + Assertions.assertTrue(desiredAdapter.getAssetIds().isEmpty()); + Assertions.assertTrue(desiredAdapter.isManaged()); + } catch (Exception e) { + System.out.println("Error occurred during test: " + e.getMessage()); + } + System.out.println("currentAdapter: " + currentAdapter); + System.out.println("desiredAdapter: " + desiredAdapter); + } + + @Test + public void testGenerateNote_regularCase() { + FintAdapterCrd primary = new FintAdapterCrd(); + FintAdapterSpec spec = new FintAdapterSpec(); + spec.setNote("Test note"); + primary.setSpec(spec); + String applicationId = "TESTAPP"; + String result = generateNote(primary, applicationId); + assertEquals("Test note\n\nDenne adapteren er automatisk opprettet og håndteres av TESTAPP", result); + } + + private String generateNote(FintAdapterCrd primary, String applicationId) { + return String.format("%s\n\n%s%s", primary.getSpec().getNote(), "Denne adapteren er automatisk opprettet og håndteres av ", applicationId.toUpperCase()); + } + + private static Adapter currentAdapter() { + return new Adapter( + "dn", + "current", + false, + "shortDescription", + new ArrayList<>(), + new ArrayList<>(), + "note", + "password", + "secret", + "publicKey", + "clientId", + new ArrayList<>(), + new ArrayList<>()); + } + private static Adapter desiredAdapter() { + return new Adapter( + "dn", + "desired", + false, + "shortDescription", + new ArrayList<>(), + new ArrayList<>(), + "note", + "password", + "secret", + "publicKey", + "clientId", + new ArrayList<>(), + new ArrayList<>()); + } +}