All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.streamthoughts.jikkou.extension.aiven.reconciler.AivenSchemaRegistrySubjectController Maven / Gradle / Ivy

The newest version!
/*
 * SPDX-License-Identifier: Apache-2.0
 * Copyright (c) The original authors
 *
 * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
 */
package io.streamthoughts.jikkou.extension.aiven.reconciler;

import static io.streamthoughts.jikkou.core.ReconciliationMode.CREATE;
import static io.streamthoughts.jikkou.core.ReconciliationMode.DELETE;
import static io.streamthoughts.jikkou.core.ReconciliationMode.FULL;
import static io.streamthoughts.jikkou.core.ReconciliationMode.UPDATE;

import io.streamthoughts.jikkou.core.ReconciliationContext;
import io.streamthoughts.jikkou.core.annotation.SupportedResource;
import io.streamthoughts.jikkou.core.extension.ExtensionContext;
import io.streamthoughts.jikkou.core.models.change.GenericResourceChange;
import io.streamthoughts.jikkou.core.models.change.GenericResourceChangeSpec;
import io.streamthoughts.jikkou.core.models.change.ResourceChange;
import io.streamthoughts.jikkou.core.reconciler.ChangeExecutor;
import io.streamthoughts.jikkou.core.reconciler.ChangeHandler;
import io.streamthoughts.jikkou.core.reconciler.ChangeResult;
import io.streamthoughts.jikkou.core.reconciler.Controller;
import io.streamthoughts.jikkou.core.reconciler.annotations.ControllerConfiguration;
import io.streamthoughts.jikkou.core.selector.Selectors;
import io.streamthoughts.jikkou.extension.aiven.ApiVersions;
import io.streamthoughts.jikkou.extension.aiven.api.AivenApiClient;
import io.streamthoughts.jikkou.extension.aiven.api.AivenApiClientConfig;
import io.streamthoughts.jikkou.extension.aiven.api.AivenApiClientFactory;
import io.streamthoughts.jikkou.extension.aiven.api.AivenAsyncSchemaRegistryApi;
import io.streamthoughts.jikkou.schema.registry.api.AsyncSchemaRegistryApi;
import io.streamthoughts.jikkou.schema.registry.change.SchemaSubjectChangeComputer;
import io.streamthoughts.jikkou.schema.registry.change.SchemaSubjectChangeDescription;
import io.streamthoughts.jikkou.schema.registry.change.handler.CreateSchemaSubjectChangeHandler;
import io.streamthoughts.jikkou.schema.registry.change.handler.DeleteSchemaSubjectChangeHandler;
import io.streamthoughts.jikkou.schema.registry.change.handler.UpdateSchemaSubjectChangeHandler;
import io.streamthoughts.jikkou.schema.registry.model.CompatibilityLevels;
import io.streamthoughts.jikkou.schema.registry.models.V1SchemaRegistrySubject;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Aiven - Schema Registry Subjects Controller.
 */
@ControllerConfiguration(
        supportedModes = {CREATE, DELETE, UPDATE, FULL}
)
@SupportedResource(
        apiVersion = ApiVersions.KAFKA_AIVEN_V1BETA1,
        kind = ApiVersions.SCHEMA_REGISTRY_KIND
)
@SupportedResource(
        apiVersion = ApiVersions.KAFKA_AIVEN_V1BETA1,
        kind = ApiVersions.SCHEMA_REGISTRY_CHANGE_KIND
)
public class AivenSchemaRegistrySubjectController implements Controller {

    private static final Logger LOG = LoggerFactory.getLogger(AivenSchemaRegistrySubjectController.class);

    private AivenApiClientConfig configuration;
    private CompatibilityLevels globalCompatibilityLevel = null;

    /**
     * Creates a new {@link AivenSchemaRegistrySubjectController} instance.
     */
    public AivenSchemaRegistrySubjectController() {
    }

    /**
     * Creates a new {@link AivenSchemaRegistrySubjectCollector} instance.
     *
     * @param config the configuration.
     */
    public AivenSchemaRegistrySubjectController(AivenApiClientConfig config) {
        init(config);
    }

    /**
     * {@inheritDoc}
     **/
    @Override
    public void init(@NotNull final ExtensionContext context) {
        init(new AivenApiClientConfig(context.appConfiguration()));
    }

    private void init(@NotNull AivenApiClientConfig configuration) {
        if (this.configuration == null) {
            this.configuration = configuration;
        }
    }

    /**
     * {@inheritDoc}
     **/
    @Override
    public List execute(@NotNull ChangeExecutor executor,
                                      @NotNull ReconciliationContext context) {
        AsyncSchemaRegistryApi api = new AivenAsyncSchemaRegistryApi(AivenApiClientFactory.create(configuration));
        try {
            List> handlers = List.of(
                    new CreateSchemaSubjectChangeHandler(api),
                    new UpdateSchemaSubjectChangeHandler(api),
                    new DeleteSchemaSubjectChangeHandler(api),
                    new ChangeHandler.None<>(SchemaSubjectChangeDescription::new)
            );
            return executor.applyChanges(handlers);
        } finally {
            api.close();
        }
    }

    /**
     * {@inheritDoc}
     **/
    @Override
    public List plan(
            @NotNull Collection resources,
            @NotNull ReconciliationContext context) {

        // Get described resources that are candidates for this reconciliation.
        List expectedSubjects = resources.stream()
                .filter(context.selector()::apply)
                .map(this::useGlobalCompatibilityLevelIfUnspecified)
                .toList();

        // Get existing resources from the environment.
        AivenSchemaRegistrySubjectCollector collector = new AivenSchemaRegistrySubjectCollector(configuration)
                .prettyPrintSchema(false);

        List actualSubjects = collector.listAll(context.configuration(), Selectors.NO_SELECTOR).stream()
                .filter(context.selector()::apply)
                .toList();

        SchemaSubjectChangeComputer computer = new SchemaSubjectChangeComputer();

        // Compute changes
        return computer.computeChanges(actualSubjects, expectedSubjects)
                .stream()
                .map(change -> GenericResourceChange
                        .builder()
                        .withApiVersion(ApiVersions.KAFKA_AIVEN_V1BETA1)
                        .withKind(ApiVersions.SCHEMA_REGISTRY_CHANGE_KIND)
                        .withMetadata(change.getMetadata())
                        .withSpec((GenericResourceChangeSpec) change.getSpec())
                        .build()
                )
                .collect(Collectors.toList());
    }

    @NotNull
    private V1SchemaRegistrySubject useGlobalCompatibilityLevelIfUnspecified(final @NotNull V1SchemaRegistrySubject item) {
        CompatibilityLevels compatibilityLevel = item.getSpec().getCompatibilityLevel();
        if (compatibilityLevel == null) {
            return item.withSpec(
                    item.getSpec()
                            .withCompatibilityLevel(getGlobalCompatibilityLevel())
            );
        }
        return item;
    }

    @Nullable
    private CompatibilityLevels getGlobalCompatibilityLevel() {
        if (globalCompatibilityLevel == null) {
            try (AivenApiClient api = AivenApiClientFactory.create(configuration);) {
                globalCompatibilityLevel = api.getSchemaRegistryGlobalCompatibility().compatibilityLevel();
            } catch (Exception e) {
                LOG.error("Failed to get to Schema Registry global compatibility level.", e);
            }
        }
        return globalCompatibilityLevel;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy