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

org.apache.camel.component.kubernetes.properties.BasePropertiesFunction Maven / Gradle / Ivy

There is a newer version: 4.9.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.component.kubernetes.properties;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;

import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.spi.PropertiesComponent;
import org.apache.camel.spi.PropertiesFunction;
import org.apache.camel.spi.PropertyConfigurer;
import org.apache.camel.support.CamelContextHelper;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.support.PropertyBindingSupport;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.util.LocationHelper;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.OrderedLocationProperties;
import org.apache.camel.util.SensitiveUtils;
import org.apache.camel.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base for kubernetes {@link PropertiesFunction}.
 */
abstract class BasePropertiesFunction extends ServiceSupport implements PropertiesFunction, CamelContextAware {

    // keys in application.properties
    public static final String CLIENT_ENABLED = "camel.kubernetes-config.client-enabled";
    public static final String LOCAL_MODE = "camel.kubernetes-config.local-mode";
    public static final String MOUNT_PATH_CONFIGMAPS = "camel.kubernetes-config.mount-path-configmaps";
    public static final String MOUNT_PATH_SECRETS = "camel.kubernetes-config.mount-path-secrets";

    // use camel-k ENV for mount paths
    public static final String JVM_PROP_MOUNT_PATH_CONFIGMAPS = "camel.k.mount-path.configmaps";
    public static final String ENV_MOUNT_PATH_CONFIGMAPS = "CAMEL_K_MOUNT_PATH_CONFIGMAPS";
    public static final String JVM_PROP_MOUNT_PATH_SECRETS = "camel.k.mount-path.secrets";
    public static final String ENV_MOUNT_PATH_SECRETS = "CAMEL_K_MOUNT_PATH_SECRETS";
    private static final Logger LOG = LoggerFactory.getLogger(BasePropertiesFunction.class);

    private static final AtomicBoolean LOGGED = new AtomicBoolean();

    private CamelContext camelContext;
    private KubernetesClient client;
    private Boolean localMode;
    private Boolean clientEnabled;
    private String mountPathConfigMaps;
    private String mountPathSecrets;

    @Override
    protected void doInit() {
        ObjectHelper.notNull(camelContext, "CamelContext");
        if (localMode == null) {
            localMode = "true"
                    .equalsIgnoreCase(camelContext.getPropertiesComponent().resolveProperty(LOCAL_MODE).orElse("false"));
        }
        if (!localMode) {
            doInitKubernetesClient();
        }
    }

    protected void doInitKubernetesClient() {
        if (clientEnabled == null) {
            clientEnabled = "true"
                    .equalsIgnoreCase(camelContext.getPropertiesComponent().resolveProperty(CLIENT_ENABLED).orElse("true"));
        }
        if (mountPathConfigMaps == null) {
            mountPathConfigMaps = camelContext.getPropertiesComponent().resolveProperty(MOUNT_PATH_CONFIGMAPS)
                    .orElseGet(
                            () -> System.getProperty(JVM_PROP_MOUNT_PATH_CONFIGMAPS, System.getenv(ENV_MOUNT_PATH_CONFIGMAPS)));
        }
        if (mountPathSecrets == null) {
            mountPathSecrets = camelContext.getPropertiesComponent().resolveProperty(MOUNT_PATH_SECRETS)
                    .orElseGet(() -> System.getProperty(JVM_PROP_MOUNT_PATH_SECRETS, System.getenv(ENV_MOUNT_PATH_SECRETS)));
        }
        if (clientEnabled && client == null) {
            client = CamelContextHelper.findSingleByType(camelContext, KubernetesClient.class);
        }
        if (clientEnabled && client == null) {
            // try to auto-configure via properties
            PropertiesComponent pc = camelContext.getPropertiesComponent();
            OrderedLocationProperties properties = (OrderedLocationProperties) pc
                    .loadProperties(k -> k.startsWith("camel.kubernetes-config.client."),
                            k -> k.replace("camel.kubernetes-config.client.", ""));
            if (!properties.isEmpty()) {
                ConfigBuilder config = new ConfigBuilder();

                PropertyConfigurer configurer = PluginHelper.getConfigurerResolver(camelContext)
                        .resolvePropertyConfigurer(ConfigBuilder.class.getName(), camelContext);

                // use copy to keep track of which options was configured or not
                OrderedLocationProperties copy = new OrderedLocationProperties();
                copy.putAll(properties);

                PropertyBindingSupport.build()
                        .withProperties(copy.asMap())
                        .withFluentBuilder(true)
                        .withIgnoreCase(true)
                        .withReflection(false)
                        .withConfigurer(configurer)
                        .withTarget(config)
                        .withCamelContext(camelContext)
                        .withRemoveParameters(true)
                        .bind();
                if (!copy.isEmpty()) {
                    // some options were not possible to configure
                    for (var e : copy.entrySet()) {
                        properties.remove(e.getKey());
                    }
                }
                client = new KubernetesClientBuilder().withConfig(config.build()).build();
                LOG.info("Auto-configuration KubernetesClient summary");
                for (var entry : properties.entrySet()) {
                    String k = entry.getKey().toString();
                    Object v = entry.getValue();
                    String loc = LocationHelper.locationSummary(properties, k);
                    if (SensitiveUtils.containsSensitive(k)) {
                        LOG.info("    {} {}=xxxxxx", loc, k);
                    } else {
                        LOG.info("    {} {}={}", loc, k, v);
                    }
                }
                if (!copy.isEmpty()) {
                    for (var e : copy.entrySet()) {
                        LOG.warn("Property not auto-configured: camel.kubernetes-config.client.{}={}", e.getKey(),
                                e.getValue());
                    }
                }
            } else {
                // create a default client to use
                client = new KubernetesClientBuilder().build();
                LOG.debug("Created default KubernetesClient (auto-configured by itself)");
            }
            // add to registry so the client can be reused
            camelContext.getRegistry().bind("camelKubernetesClient", client);
        }

        if (clientEnabled && client == null && getMountPath() == null) {
            throw new IllegalArgumentException("Either a mount path or the Kubernetes Client must be configured");
        }
        if (!clientEnabled && getMountPath() == null) {
            throw new IllegalArgumentException("Mount path must be configured");
        }
        if (client != null && LOGGED.compareAndSet(false, true)) {
            // only log once
            LOG.info("KubernetesClient using masterUrl: {} with namespace: {}", client.getMasterUrl(), client.getNamespace());
        }
    }

    @Override
    public CamelContext getCamelContext() {
        return camelContext;
    }

    @Override
    public void setCamelContext(CamelContext camelContext) {
        this.camelContext = camelContext;
    }

    public KubernetesClient getClient() {
        return client;
    }

    /**
     * To use an existing kubernetes client to use
     */
    public void setClient(KubernetesClient client) {
        this.client = client;
    }

    public boolean isClientEnabled() {
        return clientEnabled;
    }

    /**
     * Whether to use KubernetesClient to lookup from the Kubernetes API server. Is by default enabled.
     */
    public void setClientEnabled(boolean clientEnabled) {
        this.clientEnabled = clientEnabled;
    }

    public String getMountPathConfigMaps() {
        return mountPathConfigMaps;
    }

    /**
     * To use a volume mount to load configmaps (first), and fallback to using the Kubernetes API server
     */
    public void setMountPathConfigMaps(String mountPathConfigMaps) {
        this.mountPathConfigMaps = mountPathConfigMaps;
    }

    public String getMountPathSecrets() {
        return mountPathSecrets;
    }

    /**
     * To use a volume mount to load secrets (first), and fallback to using the Kubernetes API server
     */
    public void setMountPathSecrets(String mountPathSecrets) {
        this.mountPathSecrets = mountPathSecrets;
    }

    @Override
    public String apply(String remainder) {
        String defaultValue = StringHelper.after(remainder, ":");
        String name = StringHelper.before(remainder, "/");
        String key = StringHelper.after(remainder, "/");
        if (name == null || key == null) {
            return defaultValue;
        }

        // local-mode will not lookup in kubernetes but as local properties
        if (localMode) {
            String localKey = name + "/" + key;
            return getCamelContext().getPropertiesComponent().resolveProperty(localKey).orElse(defaultValue);
        }

        String answer = null;
        Path root = getMountPath();
        if (root != null) {
            Path file = root.resolve(name.toLowerCase(Locale.US)).resolve(key);
            if (Files.exists(file) && !Files.isDirectory(file)) {
                try {
                    if (isBinaryProperty()) {
                        answer = writeDataToTempFile(file.getFileName().toString(), Files.readAllBytes(file));
                    } else {
                        answer = Files.readString(file, StandardCharsets.UTF_8);
                    }
                } catch (IOException e) {
                    // ignore
                }
            }
        }

        if (answer == null && client != null) {
            answer = lookup(name, key, defaultValue);
        }
        if (answer == null) {
            answer = defaultValue;
        }

        return answer;
    }

    abstract Path getMountPath();

    abstract String lookup(String name, String key, String defaultValue);

    protected String handleData(String key, byte[] raw) {
        return isBinaryProperty() ? writeDataToTempFile(key, raw) : new String(raw);
    }

    private boolean isBinaryProperty() {
        return getName().endsWith("-binary");
    }

    protected String writeDataToTempFile(String fileName, byte[] data) {
        try {
            final Path filePath = Files.createTempDirectory("camel").resolve(fileName);
            Files.write(filePath, data);
            return filePath.toAbsolutePath().toString();
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy