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

io.radanalytics.operator.common.crd.CrdDeployer Maven / Gradle / Ivy

package io.radanalytics.operator.common.crd;

import com.fasterxml.jackson.databind.DeserializationFeature;
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition;
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinitionBuilder;
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinitionFluent;
import io.fabric8.kubernetes.api.model.apiextensions.JSONSchemaProps;
import io.fabric8.kubernetes.client.CustomResourceList;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.radanalytics.operator.common.EntityInfo;
import io.radanalytics.operator.common.JSONSchemaReader;
import org.slf4j.Logger;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Singleton
public class CrdDeployer {

    @Inject
    protected Logger log;

    public CustomResourceDefinition initCrds(KubernetesClient client,
                                                    String prefix,
                                                    String entityName,
                                                    String[] shortNames,
                                                    String pluralName,
                                                    String[] additionalPrinterColumnNames,
                                                    String[] additionalPrinterColumnPaths,
                                                    String[] additionalPrinterColumnTypes,
                                                    Class infoClass,
                                                    boolean isOpenshift) {
        final String newPrefix = prefix.substring(0, prefix.length() - 1);
        CustomResourceDefinition crdToReturn;

        Serialization.jsonMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        List crds = client.customResourceDefinitions()
                .list()
                .getItems()
                .stream()
                .filter(p -> entityName.equals(p.getSpec().getNames().getKind()) && newPrefix.equals(p.getSpec().getGroup()))
                .collect(Collectors.toList());
        if (!crds.isEmpty()) {
            crdToReturn = crds.get(0);
            log.info("CustomResourceDefinition for {} has been found in the K8s, so we are skipping the creation.", entityName);
        } else {
            log.info("Creating CustomResourceDefinition for {}.", entityName);
            JSONSchemaProps schema = JSONSchemaReader.readSchema(infoClass);
            CustomResourceDefinitionFluent.SpecNested builder;

            if (schema != null) {
                removeDefaultValues(schema);
                builder = getCRDBuilder(newPrefix,
                                        entityName,
                                        shortNames,
                                        pluralName)
                        .withNewValidation()
                        .withNewOpenAPIV3SchemaLike(schema)
                        .endOpenAPIV3Schema()
                        .endValidation();
            } else {
                builder = getCRDBuilder(newPrefix,
                                        entityName,
                                        shortNames,
                                        pluralName);
            }
            if (additionalPrinterColumnNames != null && additionalPrinterColumnNames.length > 0) {
                for (int i = 0; i < additionalPrinterColumnNames.length; i++) {
                    builder = builder.addNewAdditionalPrinterColumn().withName(additionalPrinterColumnNames[i]).withJSONPath(additionalPrinterColumnPaths[i]).endAdditionalPrinterColumn();
                }
            }
            crdToReturn = builder.endSpec().build();
            try {
                if (schema != null) {
                    // https://github.com/fabric8io/kubernetes-client/issues/1486
                    crdToReturn.getSpec().getValidation().getOpenAPIV3Schema().setDependencies(null);
                }

                client.customResourceDefinitions().createOrReplace(crdToReturn);
            } catch (KubernetesClientException e) {
                // old version of K8s/openshift -> don't use schema validation
                log.warn("Consider upgrading the {}. Your version doesn't support schema validation for custom resources."
                        , isOpenshift ? "OpenShift" : "Kubernetes");
                crdToReturn = getCRDBuilder(newPrefix,
                                            entityName,
                                            shortNames,
                                            pluralName)
                        .endSpec()
                        .build();
                client.customResourceDefinitions().createOrReplace(crdToReturn);
            }
        }

        // register the new crd for json serialization
        io.fabric8.kubernetes.internal.KubernetesDeserializer.registerCustomKind(newPrefix + "/" + crdToReturn.getSpec().getVersion() + "#" + entityName, InfoClass.class);
        io.fabric8.kubernetes.internal.KubernetesDeserializer.registerCustomKind(newPrefix + "/" + crdToReturn.getSpec().getVersion() + "#" + entityName + "List", CustomResourceList.class);

        return crdToReturn;
    }

    private void removeDefaultValues(JSONSchemaProps schema) {
        if (null == schema) {
            return;
        }
        schema.setDefault(null);
        if (null != schema.getProperties()) {
            for (JSONSchemaProps prop : schema.getProperties().values()) {
                removeDefaultValues(prop);
            }
        }
    }

    private CustomResourceDefinitionFluent.SpecNested getCRDBuilder(String prefix,
                                                                                                            String entityName,
                                                                                                            String[] shortNames,
                                                                                                            String pluralName) {
        // if no plural name is specified, try to make one by adding "s"
        // also, plural names must be all lowercase
        String plural = pluralName;
        if (plural.isEmpty()) {
            plural = (entityName + "s");
        }
        plural = plural.toLowerCase();

        // short names must be all lowercase
        String[] shortNamesLower = Arrays.stream(shortNames)
                                         .map(sn -> sn.toLowerCase())
                                         .toArray(String[]::new);

        return new CustomResourceDefinitionBuilder()
                .withApiVersion("apiextensions.k8s.io/v1beta1")
                .withNewMetadata().withName(plural + "." + prefix)
                .endMetadata()
                .withNewSpec()
                    .withNewNames()
                    .withKind(entityName)
                    .withPlural(plural)
                    .withShortNames(Arrays.asList(shortNamesLower)).endNames()
                .withGroup(prefix)
                .withVersion("v1")
                .withScope("Namespaced");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy