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

io.streamthoughts.jikkou.kafka.change.acl.AclChangeHandler 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.kafka.change.acl;

import io.streamthoughts.jikkou.common.utils.Pair;
import io.streamthoughts.jikkou.core.data.TypeConverter;
import io.streamthoughts.jikkou.core.models.change.GenericStateChange;
import io.streamthoughts.jikkou.core.models.change.ResourceChange;
import io.streamthoughts.jikkou.core.models.change.SpecificStateChange;
import io.streamthoughts.jikkou.core.reconciler.ChangeMetadata;
import io.streamthoughts.jikkou.core.reconciler.ChangeResponse;
import io.streamthoughts.jikkou.core.reconciler.Operation;
import io.streamthoughts.jikkou.core.reconciler.TextDescription;
import io.streamthoughts.jikkou.core.reconciler.change.BaseChangeHandler;
import io.streamthoughts.jikkou.kafka.model.KafkaAclBinding;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.CreateAclsResult;
import org.apache.kafka.clients.admin.DeleteAclsResult;
import org.apache.kafka.common.acl.AclBinding;
import org.apache.kafka.common.acl.AclBindingFilter;
import org.jetbrains.annotations.NotNull;

public final class AclChangeHandler extends BaseChangeHandler {

    private final AdminClient client;

    /**
     * Creates a new {@link AclChangeHandler} instance.
     *
     * @param client the {@link AdminClient}.
     */
    public AclChangeHandler(@NotNull final AdminClient client) {
        super(Set.of(Operation.CREATE, Operation.UPDATE, Operation.DELETE));
        this.client = Objects.requireNonNull(client, "client cannot not be null");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List> handleChanges(@NotNull final List changes) {
        return changes.stream()
                .map(change -> {
                    Map>> changesByOperation = change
                        .getSpec()
                        .getChanges()
                        .get(AclChangeComputer.ACL)
                        .all(TypeConverter.of(KafkaAclBinding.class))
                        .stream()
                        .collect(Collectors.groupingBy(GenericStateChange::getOp));

                    List bindingsForCreate = changesByOperation
                        .getOrDefault(Operation.CREATE, List.of())
                        .stream()
                        .map(SpecificStateChange::getAfter)
                        .map(KafkaAclBinding::toAclBinding)
                        .toList();

                    List bindingsForDelete = changesByOperation
                        .getOrDefault(Operation.DELETE, List.of())
                        .stream()
                        .map(SpecificStateChange::getBefore)
                        .map(KafkaAclBinding::toAclBindingFilter)
                        .toList();

                    List> futures = new LinkedList<>();

                    if (!bindingsForCreate.isEmpty()) {
                        CreateAclsResult resultForCreate = client.createAcls(bindingsForCreate);
                        List> completableFutures = resultForCreate
                            .values()
                            .entrySet()
                            .stream()
                            .map(e -> Pair.of(e.getKey(), e.getValue()))
                            .map(pair -> pair._2()
                                .toCompletionStage()
                                .toCompletableFuture()
                                .thenApply(ignore -> ChangeMetadata.empty())

                            ).toList();
                        futures.addAll(completableFutures);
                    }

                    if (!bindingsForDelete.isEmpty()) {
                        DeleteAclsResult resultForDelete = client.deleteAcls(bindingsForDelete);
                        List> completableFutures = resultForDelete
                            .values()
                            .entrySet()
                            .stream()
                            .map(e -> Pair.of(e.getKey(), e.getValue()))
                            .map(pair -> pair._2()
                                .toCompletionStage()
                                .toCompletableFuture()
                                .thenApply(ignore -> ChangeMetadata.empty())

                            )
                            .toList();
                    }
                    return new ChangeResponse<>(change, futures);
                })
                .toList();
    }

    /**
     * {@inheritDoc}
     **/
    @Override
    public TextDescription describe(@NotNull ResourceChange change) {
        return new KafkaPrincipalAuthorizationDescription(change);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy