VolumePermissionEnricher extends BaseEnricher { @Override public void enrich(PlatformMode platformMode, KubernetesListBuilder builder) { builder.accept(new TypedVisitor<PodTemplateSpecBuilder>() { @Override public void visit(PodTemplateSpecBuilder builder) { PodSpec podSpec = builder.buildSpec(); if (podSpec == null) { return; } if (!checkForPvc(podSpec)) { return; } List<Container> containers = podSpec.getContainers(); if (containers == null || containers.isEmpty()) { return; } log.verbose(Logger.LogVerboseCategory.BUILD, "Adding init container for changing persistent volumes access mode to %s", getConfig(Config.permission)); if (!initContainerHandler.hasInitContainer(builder, ENRICHER_NAME)) { initContainerHandler.appendInitContainer(builder, createPvInitContainer(podSpec)); } } private boolean checkForPvc(PodSpec podSpec) { List<Volume> volumes = podSpec.getVolumes(); if (volumes != null) { for (Volume volume : volumes) { PersistentVolumeClaimVolumeSource persistentVolumeClaim = volume.getPersistentVolumeClaim(); if (persistentVolumeClaim != null) { return true; } } } return false; } private Container createPvInitContainer(PodSpec podSpec) { Map<String, String> mountPoints = extractMountPoints(podSpec); return new ContainerBuilder() .withName(ENRICHER_NAME) .withImage("busybox") .withImagePullPolicy("IfNotPresent") .withCommand(createChmodCommandArray(mountPoints)) .withVolumeMounts(createMounts(mountPoints)) .build(); } private List<String> createChmodCommandArray(Map<String, String> mountPoints) { List<String> ret = new ArrayList<>(); ret.add("chmod"); ret.add(getConfig(Config.permission)); Set<String> uniqueNames = new LinkedHashSet<>(mountPoints.values()); for (String name : uniqueNames) { ret.add(name); } return ret; } private List<VolumeMount> createMounts(Map<String, String> mountPoints) { List<VolumeMount> ret = new ArrayList<>(); for (Map.Entry<String, String> entry : mountPoints.entrySet()) { JsonObject mount = new JsonObject(); mount.add("name", new JsonPrimitive(entry.getKey())); mount.add("mountPath", new JsonPrimitive(entry.getValue())); VolumeMount volumeMount = new VolumeMountBuilder() .withName(entry.getKey()) .withMountPath(entry.getValue()) .build(); ret.add(volumeMount); } return ret; } private Map<String, String> extractMountPoints(PodSpec podSpec) { Map<String, String> nameToMount = new LinkedHashMap<>(); List<Volume> volumes = podSpec.getVolumes(); if (volumes != null) { for (Volume volume : volumes) { PersistentVolumeClaimVolumeSource persistentVolumeClaim = volume.getPersistentVolumeClaim(); if (persistentVolumeClaim != null) { String name = volume.getName(); String mountPath = getMountPath(podSpec.getContainers(), name); nameToMount.put(name, mountPath); } } } return nameToMount; } private String getMountPath(List<Container> containers, String name){ for (Container container : containers) { List<VolumeMount> volumeMounts = container.getVolumeMounts(); if (volumeMounts != null) { for (VolumeMount volumeMount : volumeMounts) { if (name.equals(volumeMount.getName())){ return volumeMount.getMountPath(); } } } } throw new IllegalArgumentException("No matching volume mount found for volume "+ name); } }); builder.accept(new TypedVisitor<PersistentVolumeClaimBuilder>() { @Override public void visit(PersistentVolumeClaimBuilder pvcBuilder) { if (pvcBuilder.buildMetadata() == null) { pvcBuilder.withNewMetadata().endMetadata(); } String storageClass = getConfig(Config.defaultStorageClass); if (StringUtils.isNotBlank(storageClass)) { pvcBuilder.editMetadata().addToAnnotations(VOLUME_STORAGE_CLASS_ANNOTATION, storageClass).endMetadata(); } } }); } VolumePermissionEnricher(MavenEnricherContext buildContext); @Override void enrich(PlatformMode platformMode, KubernetesListBuilder builder); static final String ENRICHER_NAME; }
@Test public void alreadyExistingInitContainer(@Mocked final ProcessorConfig config) throws Exception { new Expectations() {{ context.getConfiguration(); result = new Configuration.Builder().processorConfig(config).build(); }}; PodTemplateBuilder ptb = createEmptyPodTemplate(); addVolume(ptb, "VolumeA"); Container initContainer = new ContainerBuilder() .withName(VolumePermissionEnricher.ENRICHER_NAME) .withVolumeMounts(new VolumeMountBuilder().withName("vol-blub").withMountPath("blub").build()) .build(); ptb.editTemplate().editSpec().withInitContainers(Collections.singletonList(initContainer)).endSpec().endTemplate(); KubernetesListBuilder klb = new KubernetesListBuilder().addToPodTemplateItems(ptb.build()); VolumePermissionEnricher enricher = new VolumePermissionEnricher(context); enricher.enrich(PlatformMode.kubernetes,klb); List<Container> initS = ((PodTemplate) klb.build().getItems().get(0)).getTemplate().getSpec().getInitContainers(); assertNotNull(initS); assertEquals(1, initS.size()); Container actualInitContainer = initS.get(0); assertEquals("blub", actualInitContainer.getVolumeMounts().get(0).getMountPath()); }
@Test public void testAdapt() throws Exception { final TestConfig[] data = new TestConfig[]{ new TestConfig(null, null), new TestConfig(null, VolumePermissionEnricher.ENRICHER_NAME, "volumeA"), new TestConfig(null, VolumePermissionEnricher.ENRICHER_NAME, "volumeA", "volumeB") }; for (final TestConfig tc : data) { final ProcessorConfig config = new ProcessorConfig(null, null, Collections.singletonMap(VolumePermissionEnricher.ENRICHER_NAME, new TreeMap(Collections .singletonMap(VolumePermissionEnricher.Config.permission.name(), tc.permission)))); new Expectations() {{ context.getConfiguration(); result = new Configuration.Builder().processorConfig(config).build(); }}; VolumePermissionEnricher enricher = new VolumePermissionEnricher(context); PodTemplateBuilder ptb = createEmptyPodTemplate(); for (String vn : tc.volumeNames) { ptb = addVolume(ptb, vn); } KubernetesListBuilder klb = new KubernetesListBuilder().addToPodTemplateItems(ptb.build()); enricher.enrich(PlatformMode.kubernetes,klb); PodTemplate pt = (PodTemplate) klb.buildItem(0); List<Container> initContainers = pt.getTemplate().getSpec().getInitContainers(); boolean shouldHaveInitContainer = tc.volumeNames.length > 0; assertEquals(shouldHaveInitContainer, !initContainers.isEmpty()); if (!shouldHaveInitContainer) { continue; } Gson gson = new Gson(); JsonArray ja = new JsonParser().parse(gson.toJson(initContainers, new TypeToken<Collection<Container>>() {}.getType())).getAsJsonArray(); assertEquals(1, ja.size()); JsonObject jo = ja.get(0).getAsJsonObject(); assertEquals(tc.initContainerName, jo.get("name").getAsString()); String permission = StringUtils.isBlank(tc.permission) ? "777" : tc.permission; JsonArray chmodCmd = new JsonArray(); chmodCmd.add("chmod"); chmodCmd.add(permission); for (String vn : tc.volumeNames) { chmodCmd.add("/tmp/" + vn); } assertEquals(chmodCmd.toString(), jo.getAsJsonArray("command").toString()); } }