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

me.snowdrop.cloud.fabric8.IstioEnricher Maven / Gradle / Ivy

Go to download

The Enricher that wil be used with fabric8-maven-plugin to enrich resources with Istio resources

The newest version!
package me.snowdrop.cloud.fabric8;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import io.fabric8.kubernetes.api.builder.TypedVisitor;
import io.fabric8.kubernetes.api.model.CapabilitiesBuilder;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.api.model.EmptyDirVolumeSourceBuilder;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarSource;
import io.fabric8.kubernetes.api.model.KubernetesListBuilder;
import io.fabric8.kubernetes.api.model.ObjectFieldSelector;
import io.fabric8.kubernetes.api.model.PodSpecBuilder;
import io.fabric8.kubernetes.api.model.ResourceRequirements;
import io.fabric8.kubernetes.api.model.SecretVolumeSourceBuilder;
import io.fabric8.kubernetes.api.model.SecurityContextBuilder;
import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeBuilder;
import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.VolumeMountBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.maven.core.access.ClusterAccess;
import io.fabric8.maven.core.handler.DeploymentHandler;
import io.fabric8.maven.core.handler.HandlerHub;
import io.fabric8.maven.core.util.Configs;
import io.fabric8.maven.core.util.MavenUtil;
import io.fabric8.maven.enricher.api.BaseEnricher;
import io.fabric8.maven.enricher.api.EnricherContext;
import io.fabric8.openshift.api.model.DeploymentConfigBuilder;
import io.fabric8.openshift.api.model.DeploymentTriggerPolicy;
import io.fabric8.openshift.api.model.DeploymentTriggerPolicyBuilder;
import io.fabric8.openshift.api.model.ImageStream;
import io.fabric8.openshift.api.model.ImageStreamBuilder;
import io.fabric8.utils.Strings;
import me.snowdrop.istio.api.model.v1.mesh.AuthenticationPolicy;
import me.snowdrop.istio.api.model.v1.mesh.MeshConfig;
import me.snowdrop.istio.api.model.v1.mesh.ProxyConfig;

/**
 * This enricher takes care of adding Istio related enrichments to the Kubernetes Deployment
 * 

* TODO: once f8-m-p model has initContainers model , then annotations can be moved to templateSpec * * @author kameshs * @author charles moulliard */ public class IstioEnricher extends BaseEnricher { private static final String ISTIO_ANNOTATION_STATUS = "injected-version-releng@0d29a2c0d15f-VERSION-998e0e00d375688bcb2af042fc81a60ce5264009"; private final DeploymentHandler deployHandler; private String clusterName; private KubernetesClient kubeClient; // Available configuration keys private enum Config implements Configs.Key { name("name"), enableCoreDump("yes"), withDebugImage("true"), istioVersion("0.6.0"), istioNamespace("istio-system"), istioConfigMapName("istio"), alpineVersion("3.5"), controlPlaneAuthPolicy("NONE"), proxyName("istio-proxy"), proxyDockerImageName("docker.io/istio/proxy"), proxyImageStreamName("proxy"), initName("istio-init"), initDockerImageName("docker.io/istio/proxy_init"), initImageStreamName("proxy_init"), coreDumpName("enable-core-dump"), coreDumpDockerImageName("alpine"), coreDumpImageStreamName("alpine"), imagePullPolicy("IfNotPresent"), replicaCount("1"); public String def() { return d; } Config(String d) { this.d = d; } private final String d; } public IstioEnricher(EnricherContext buildContext) { super(buildContext, "fmp-istio-enricher"); HandlerHub handlerHub = new HandlerHub(buildContext.getProject()); deployHandler = handlerHub.getDeploymentHandler(); kubeClient = new ClusterAccess(getConfig(Config.istioNamespace)).createDefaultClient(log); } @Override public void addMissingResources(KubernetesListBuilder builder) { // first check that we actually know the requested Istio version final String istioVersion = getConfig(Config.istioVersion); final String proxyArgsTemplate = ProxyArgs.findByRelease(istioVersion); if (proxyArgsTemplate == null) { throw new IllegalArgumentException("Unknown Istio release: " + istioVersion); } clusterName = getConfig(Config.name, MavenUtil.createDefaultResourceName(getProject())); final MeshConfig meshConfig = fetchConfigMap(kubeClient, getConfig(Config.istioNamespace)); final ProxyConfig config = meshConfig.getDefaultConfig(); // check that configured authentication policy matches what's in the configmap final AuthenticationPolicy controlPlaneAuthPolicy = config.getControlPlaneAuthPolicy() == null ? AuthenticationPolicy.NONE : config.getControlPlaneAuthPolicy(); final String configuredControlPlaneAuthPolicy = getConfig(Config.controlPlaneAuthPolicy); try { AuthenticationPolicy policy = AuthenticationPolicy.valueOf(configuredControlPlaneAuthPolicy); if (!controlPlaneAuthPolicy.equals(policy)) { final String msg = "Configured AuthenticationPolicy %s via 'controlPlaneAuthPolicy' parameter doesn't match Istio ConfigMap configuration %s"; throw new IllegalArgumentException(String.format(msg, policy, controlPlaneAuthPolicy)); } } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Unknown AuthenticationPolicy for 'controlPlaneAuthPolicy' parameter"); } // replace placeholders in proxy args template final String proxyArgs = String.format( proxyArgsTemplate, clusterName, config.getDiscoveryAddress(), config.getZipkinAddress(), config.getStatsdUdpAddress(), controlPlaneAuthPolicy.toString() ); List sidecarArgs = Arrays.asList(proxyArgs.split(",")); builder.accept(new TypedVisitor() { public void visit(PodSpecBuilder podSpecBuilder) { String serviceAccountName = getServiceAccountName(podSpecBuilder); podSpecBuilder // Add Istio Proxy .addNewContainer() .withName(getConfig(Config.proxyName)) .withResources(new ResourceRequirements()) .withTerminationMessagePath("/dev/termination-log") .withImage(getConfig(Config.proxyImageStreamName)) .withImagePullPolicy(getConfig(Config.imagePullPolicy)) .withArgs(sidecarArgs) .withEnv(proxyEnvVars()) .withSecurityContext(new SecurityContextBuilder() .withRunAsUser(1337l) .withPrivileged(true) .withReadOnlyRootFilesystem(false) .build()) .withVolumeMounts(istioVolumeMounts()) .endContainer() // Specify Istio volumes .withVolumes(istioVolumes(serviceAccountName)) // Add Istio Init container and Core Dump if enabled .withInitContainers(populateInitContainers()); } }); // Add Missing triggers builder.accept(new TypedVisitor() { public void visit(DeploymentConfigBuilder deploymentConfigBuilder) { deploymentConfigBuilder .editOrNewSpec() // Add Istio Side car annotation .editOrNewTemplate() .editOrNewMetadata() .addToAnnotations("sidecar.istio.io/status", ISTIO_ANNOTATION_STATUS.replace("VERSION", istioVersion)) .endMetadata() .endTemplate() // Specify the replica count .withReplicas(Integer.parseInt(getConfig(Config.replicaCount))) .withTriggers(populateTriggers(istioVersion)) .endSpec(); } }); // TODO - Check if it already exists before to add it to the Kubernetes List // Add ImageStreams about Istio Proxy, Istio Init and Core Dump builder.addAllToImageStreamItems(istioImageStream(istioVersion)).build(); } private List populateTriggers(String istioVersion) { List triggers = new ArrayList<>(); DeploymentTriggerPolicyBuilder trigger = new DeploymentTriggerPolicyBuilder(); // Add Istio Init Image trigger.withType("ImageChange") .withNewImageChangeParams() .withAutomatic(true) .withNewFrom() .withKind("ImageStreamTag") .withName(getConfig(Config.initImageStreamName) + ":" + istioVersion) .endFrom() .withContainerNames(getConfig(Config.initName)) .endImageChangeParams() .build(); triggers.add(trigger.build()); // Add Istio Proxy Image trigger.withType("ImageChange") .withNewImageChangeParams() .withAutomatic(true) .withNewFrom() .withKind("ImageStreamTag") .withName(istioImageName(getConfig(Config.proxyImageStreamName)) + ":" + istioVersion) .endFrom() .withContainerNames(getConfig(Config.proxyName)) .endImageChangeParams() .build(); triggers.add(trigger.build()); // Add Core Dump image if enableCoreDump if ("true".equalsIgnoreCase(getConfig(Config.enableCoreDump))) { trigger.withType("ImageChange") .withNewImageChangeParams() .withAutomatic(true) .withNewFrom() .withKind("ImageStreamTag") .withName(getConfig(Config.coreDumpImageStreamName) + ":" + getConfig(Config.alpineVersion)) .endFrom() .withContainerNames("enable-core-dump") .endImageChangeParams() .build(); triggers.add(trigger.build()); } // Add Trigger to include the Microservice app trigger.withType("ImageChange") .withNewImageChangeParams() .withAutomatic(true) .withNewFrom() .withKind("ImageStreamTag") .withName(clusterName + ":latest") .endFrom() .withContainerNames("spring-boot") .endImageChangeParams() .build(); triggers.add(trigger.build()); return triggers; } protected List populateInitContainers() { List initcontainerList = new ArrayList<>(); // Add Istio container which setup IPTABLES initcontainerList.add(istioInitContainer()); if ("true".equalsIgnoreCase(getConfig(Config.enableCoreDump))) { initcontainerList.add(coreDumpInitContainer()); } return initcontainerList; } private MeshConfig fetchConfigMap(KubernetesClient kubeClient, String namespace) { final String configMapName = getConfig(Config.istioConfigMapName); ConfigMap map = kubeClient.configMaps().withName(configMapName).get(); if (map == null) { throw new IllegalArgumentException("Couldn't find an ConfigMap named " + configMapName + " in namespace " + namespace + ". Are you sure Istio was installed correctly?"); } final YAMLMapper mapper = new YAMLMapper(); final String meshConfigAsString = map.getData().get("mesh"); if (meshConfigAsString != null) { try { final MeshConfig meshConfig = mapper.readValue(meshConfigAsString, MeshConfig.class); return meshConfig; } catch (IOException e) { throw new IllegalArgumentException("Couldn't parse Istio Mesh configuration", e); } } else { throw new IllegalArgumentException("Couldn't find an Istio Mesh configuration in " + configMapName + " ConfigMap in namespace " + namespace); } } /* * kind: ImageStream metadata: generation: 1 name: proxy_debug namespace: demo spec: tags: - from: kind: DockerImage name: docker.io/istio/proxy_debug:0.2.12 generation: 1 name: latest referencePolicy: type: Source */ private List istioImageStream(String istioVersion) { List imageStreams = new ArrayList<>(); ImageStreamBuilder imageStreamBuilder = new ImageStreamBuilder(); imageStreamBuilder .withNewMetadata() .withName(getConfig(Config.initImageStreamName)) .endMetadata() .withNewSpec() .addNewTag() .withNewFrom() .withKind("DockerImage") .withName(getConfig(Config.initDockerImageName) + ":" + istioVersion) .endFrom() .withName(istioVersion) .endTag() .endSpec() .build(); imageStreams.add(imageStreamBuilder.build()); if ("true".equalsIgnoreCase(getConfig(Config.enableCoreDump))) { imageStreamBuilder = new ImageStreamBuilder(); imageStreamBuilder .withNewMetadata() .withName(getConfig(Config.coreDumpImageStreamName)) .endMetadata() .withNewSpec() .addNewTag() .withNewFrom() .withKind("DockerImage") .withName(getConfig(Config.coreDumpDockerImageName) + ":" + getConfig(Config.alpineVersion)) .endFrom() .withName(getConfig(Config.alpineVersion)) .endTag() .endSpec() .build(); imageStreams.add(imageStreamBuilder.build()); } imageStreamBuilder = new ImageStreamBuilder(); imageStreamBuilder .withNewMetadata() .withName(istioImageName(getConfig(Config.proxyImageStreamName))) .endMetadata() .withNewSpec() .addNewTag() .withNewFrom() .withKind("DockerImage") .withName(istioImageName(getConfig(Config.proxyDockerImageName)) + ":" + istioVersion) .endFrom() .withName(istioVersion) .endTag() .endSpec() .build(); imageStreams.add(imageStreamBuilder.build()); return imageStreams; } private String getServiceAccountName(PodSpecBuilder podSpecBuilder) { if (Strings.isNotBlank(podSpecBuilder.getServiceAccountName())) { return podSpecBuilder.getServiceAccountName(); } else if (Strings.isNotBlank(podSpecBuilder.getServiceAccount())) { return podSpecBuilder.getServiceAccount(); } return "default"; } protected String istioImageName(String dockerImage) { StringBuilder name = new StringBuilder(dockerImage); name = "true".equalsIgnoreCase(getConfig(Config.withDebugImage)) ? name.append("_debug") : name; return name.toString(); } protected Container istioInitContainer() { /* .put("name", "istio-init") .put("image", getConfig(Config.initImageStreamName)) .put("imagePullPolicy", "IfNotPresent") .put("resources", new JsonObject()) .put("terminationMessagePath", "/dev/termination-log") .put("terminationMessagePolicy", "File") .put("args", new JsonArray() .add("-p") .add("15001") .add("-u") .add("1337")) .put("securityContext", new JsonObject() .put("capabilities", new JsonObject() .put("add", new JsonArray().add("NET_ADMIN"))) .put("privileged",true)); */ return new ContainerBuilder() .withName(getConfig(Config.initName)) .withImage(getConfig(Config.initImageStreamName)) .withImagePullPolicy("IfNotPresent") .withTerminationMessagePath("/dev/termination-log") .withTerminationMessagePolicy("File") .withArgs("-p", "15001", "-u", "1337") .withSecurityContext(new SecurityContextBuilder() .withPrivileged(true) .withCapabilities(new CapabilitiesBuilder() .addToAdd("NET_ADMIN") .build()) .build()) .build(); } protected Container coreDumpInitContainer() { /* Enable Core Dump * args: * - '-c' * - >- * sysctl -w kernel.core_pattern=/etc/istio/proxy/core.%e.%p.%t && * ulimit -c unlimited * command: * - /bin/sh * image: alpine * imagePullPolicy: IfNotPresent * name: enable-core-dump * resources: {} * securityContext: * privileged: true * terminationMessagePath: /dev/termination-log * terminationMessagePolicy: File */ return new ContainerBuilder() .withName(getConfig(Config.coreDumpName)) .withImage(getConfig(Config.coreDumpImageStreamName)) .withImagePullPolicy("IfNotPresent") .withCommand("/bin/sh") .withArgs("-c", "sysctl -w kernel.core_pattern=/etc/istio/proxy/core.%e.%p.%t && ulimit -c unlimited") .withTerminationMessagePath("/dev/termination-log") .withTerminationMessagePolicy("File") .withSecurityContext(new SecurityContextBuilder() .withPrivileged(true) .build()) .build(); } /** * Generate the volumes to be mounted * * @return - list of {@link VolumeMount} */ protected List istioVolumeMounts() { List volumeMounts = new ArrayList<>(); VolumeMountBuilder istioProxyVolume = new VolumeMountBuilder(); istioProxyVolume .withMountPath("/etc/istio/proxy") .withName("istio-envoy") .build(); VolumeMountBuilder istioCertsVolume = new VolumeMountBuilder(); istioCertsVolume .withMountPath("/etc/certs") .withName("istio-certs") .withReadOnly(true) .build(); volumeMounts.add(istioProxyVolume.build()); volumeMounts.add(istioCertsVolume.build()); return volumeMounts; } /** * Generate the volumes * * @return - list of {@link Volume} */ protected List istioVolumes(String serviceAccountName) { List volumes = new ArrayList<>(); VolumeBuilder empTyVolume = new VolumeBuilder(); empTyVolume.withEmptyDir(new EmptyDirVolumeSourceBuilder() .withMedium("Memory") .build()) .withName("istio-envoy") .build(); VolumeBuilder secretVolume = new VolumeBuilder(); secretVolume .withName("istio-certs") .withSecret(new SecretVolumeSourceBuilder() .withSecretName("istio." + serviceAccountName) .withDefaultMode(420) .build()) .build(); volumes.add(empTyVolume.build()); volumes.add(secretVolume.build()); return volumes; } /** * The method to return list of environment variables that will be needed for Istio proxy * * @return - list of {@link EnvVar} */ protected List proxyEnvVars() { List envVars = new ArrayList<>(); //POD_NAME EnvVarSource podNameVarSource = new EnvVarSource(); podNameVarSource.setFieldRef(new ObjectFieldSelector(null, "metadata.name")); envVars.add(new EnvVar("POD_NAME", null, podNameVarSource)); //POD_NAMESPACE EnvVarSource podNamespaceVarSource = new EnvVarSource(); podNamespaceVarSource.setFieldRef(new ObjectFieldSelector(null, "metadata.namespace")); envVars.add(new EnvVar("POD_NAMESPACE", null, podNamespaceVarSource)); //POD_IP EnvVarSource podIpVarSource = new EnvVarSource(); podIpVarSource.setFieldRef(new ObjectFieldSelector(null, "status.podIP")); envVars.add(new EnvVar("INSTANCE_IP", null, podIpVarSource)); return envVars; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy