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

io.fabric8.maven.enricher.standard.VolumePermissionEnricher Maven / Gradle / Ivy

/*
 * Copyright 2016 Red Hat, Inc.
 *
 * Red Hat 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 io.fabric8.maven.enricher.standard;

import java.lang.reflect.Method;
import java.util.*;

import io.fabric8.kubernetes.api.KubernetesHelper;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.maven.core.util.Configs;
import io.fabric8.maven.enricher.api.BaseEnricher;
import io.fabric8.maven.enricher.api.EnricherContext;
import io.fabric8.utils.Lists;
import org.json.JSONArray;
import org.json.JSONObject;

/**
 * @author roland
 * @since 14/11/16
 */
public class VolumePermissionEnricher extends BaseEnricher {

    public static final String VOLUME_STORAGE_CLASS_ANNOTATION = "volume.beta.kubernetes.io/storage-class";
    public static final String INIT_CONTAINER_ANNOTATION = "pod.alpha.kubernetes.io/init-containers";

    static Set POD_TEMPLATE_SPEC_HOLDER =
        new HashSet<>(Arrays.asList("Deployment", "ReplicaSet", "ReplicationController", "DeploymentConfig"));

    private enum Config implements Configs.Key {
        permission {{ d = "777"; }};

        public String def() { return d; } protected String d;
    }

    public VolumePermissionEnricher(EnricherContext buildContext) {
        super(buildContext, "fmp-volume-permission");
    }

    @Override
    public void adapt(KubernetesListBuilder builder) {
        for (HasMetadata item : Lists.notNullList(builder.getItems())) {
            if (POD_TEMPLATE_SPEC_HOLDER.contains(item.getKind())) {
                addPersistentVolumeInitContainerChmod(item, getPodTemplateSpec(item));
            } else if ("PersistentVolumeClaim".equals(item.getKind())) {
                ensureVolumeStorageClass((PersistentVolumeClaim) item);
            }
        }
        super.adapt(builder);
    }

    private void ensureVolumeStorageClass(PersistentVolumeClaim pvc) {
        // lets ensure we have a default storage class so that PVs will get dynamically created OOTB
        Map annotations = KubernetesHelper.getOrCreateAnnotations(pvc);
        if (!annotations.containsKey(VOLUME_STORAGE_CLASS_ANNOTATION)) {
            annotations.put(VOLUME_STORAGE_CLASS_ANNOTATION, "standard");
        }
    }


    private void addPersistentVolumeInitContainerChmod(HasMetadata entity, PodTemplateSpec template) {
        if (template == null) {
            return;
        }

        PodSpec podSpec = template.getSpec();
        if (podSpec == null) {
            return;
        }

        if (!checkForPvc(podSpec)) {
            return;
        }

        List containers = podSpec.getContainers();
        if (containers == null) {
            return;
        }

        String pvAnnotation = createPvAnnotation(containers);
        log.verbose("Adding annotation %s for changing persistent volumes access mode to %s",
                    INIT_CONTAINER_ANNOTATION, getConfig(Config.permission) );

        ObjectMeta metadata = ensureMetadata(template);
        Map annotations = ensureAnnotations(metadata);

        annotations.put(INIT_CONTAINER_ANNOTATION, pvAnnotation);
    }

    private Map ensureAnnotations(ObjectMeta metadata) {
        Map annotations = metadata.getAnnotations();
        if (annotations == null) {
            annotations = new HashMap();
            metadata.setAnnotations(annotations);
        } return annotations;
    }

    private ObjectMeta ensureMetadata(PodTemplateSpec template) {
        ObjectMeta metadata = template.getMetadata();
        if (metadata == null) {
            metadata = new ObjectMeta();
            template.setMetadata(metadata);
        }
        return metadata;
    }

    private String createPvAnnotation(List containers) {
        Map mountPoints = extractMountPoints(containers);

        JSONArray anno = new JSONArray();
        JSONObject entry = new JSONObject();
        entry.put("name","init");
        entry.put("image","busybox");
        entry.put("imagePullPolicy","IfNotPresent");
        entry.put("command", createChmodCommandArray(mountPoints));
        entry.put("volumeMounts", createMounts(mountPoints));
        anno.put(entry);
        return anno.toString();
    }

    private JSONArray createChmodCommandArray(Map mountPoints) {
        JSONArray ret = new JSONArray();
        ret.put("chmod");
        ret.put(getConfig(Config.permission));
        Set uniqueNames = new HashSet(mountPoints.values());
        for (String name : uniqueNames) {
            ret.put(name);
        }
        return ret;
    }

    private JSONArray createMounts(Map mountPoints) {
        JSONArray ret = new JSONArray();
        for (Map.Entry entry : mountPoints.entrySet()) {
            JSONObject mount = new JSONObject();
            mount.put("name", entry.getKey());
            mount.put("mountPath", entry.getValue());
            ret.put(mount);
        }
        return ret;
    }

    private Map extractMountPoints(List containers) {
        Map nameToMount = new TreeMap<>();
        for (Container container : containers) {
            List volumeMounts = container.getVolumeMounts();
            if (volumeMounts != null) {
                for (VolumeMount volumeMount : volumeMounts) {
                    String name = volumeMount.getName();
                    if (!nameToMount.containsKey(name)) {
                        nameToMount.put(name, volumeMount.getMountPath());
                    }
                }
            }
        }
        return nameToMount;
    }

    private boolean checkForPvc(PodSpec podSpec) {
        List volumes = podSpec.getVolumes();
        if (volumes != null) {
            for (Volume volume : volumes) {
                PersistentVolumeClaimVolumeSource persistentVolumeClaim = volume.getPersistentVolumeClaim();
                if (persistentVolumeClaim != null) {
                    return true;
                }
            }
        }
        return false;
    }

    private PodTemplateSpec getPodTemplateSpec(HasMetadata item) {
        try {
            Method specGet = item.getClass().getMethod("getSpec");
            Object spec = specGet.invoke(item);
            if (spec != null) {
                Method templateGet = spec.getClass().getMethod("getTemplate");
                return (PodTemplateSpec) templateGet.invoke(spec);
            } else {
                return null;
            }
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException("Cannot extract pod template from " + item + ": " + e,e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy