PodMerger { public Deployment merge(List<PodData> podsData) throws ValidationException { Deployment baseDeployment = createEmptyDeployment(DEFAULT_DEPLOYMENT_NAME); PodTemplateSpec basePodTemplate = baseDeployment.getSpec().getTemplate(); ObjectMeta basePodMeta = basePodTemplate.getMetadata(); PodSpec baseSpec = basePodTemplate.getSpec(); Set<String> containerNames = new HashSet<>(); Set<String> initContainerNames = new HashSet<>(); Set<String> volumes = new HashSet<>(); Set<String> pullSecrets = new HashSet<>(); for (PodData podData : podsData) { ObjectMeta podMeta = podData.getMetadata(); putLabels(basePodMeta, podMeta.getLabels()); putAnnotations(basePodMeta, podMeta.getAnnotations()); basePodMeta.getAdditionalProperties().putAll(podMeta.getAdditionalProperties()); for (Container container : podData.getSpec().getContainers()) { String containerName = container.getName(); while (!containerNames.add(container.getName())) { containerName = NameGenerator.generate(container.getName(), 4); container.setName(containerName); } Names.putMachineName(basePodMeta, containerName, Names.machineName(podMeta, container)); baseSpec.getContainers().add(container); } for (Container initContainer : podData.getSpec().getInitContainers()) { while (!initContainerNames.add(initContainer.getName())) { initContainer.setName(NameGenerator.generate(initContainer.getName(), 4)); } baseSpec.getInitContainers().add(initContainer); } for (Volume volume : podData.getSpec().getVolumes()) { if (!volumes.add(volume.getName())) { if (volume.getName().equals(PROJECTS_VOLUME_NAME)) { continue; } throw new ValidationException( format( "Pods have to have volumes with unique names but there are multiple `%s` volumes", volume.getName())); } baseSpec.getVolumes().add(volume); } for (LocalObjectReference pullSecret : podData.getSpec().getImagePullSecrets()) { if (pullSecrets.add(pullSecret.getName())) { baseSpec.getImagePullSecrets().add(pullSecret); } } if (podData.getSpec().getTerminationGracePeriodSeconds() != null) { if (baseSpec.getTerminationGracePeriodSeconds() != null) { baseSpec.setTerminationGracePeriodSeconds( Long.max( baseSpec.getTerminationGracePeriodSeconds(), podData.getSpec().getTerminationGracePeriodSeconds())); } else { baseSpec.setTerminationGracePeriodSeconds( podData.getSpec().getTerminationGracePeriodSeconds()); } } baseSpec.setSecurityContext( mergeSecurityContexts( baseSpec.getSecurityContext(), podData.getSpec().getSecurityContext())); baseSpec.setServiceAccount( mergeServiceAccount(baseSpec.getServiceAccount(), podData.getSpec().getServiceAccount())); baseSpec.setServiceAccountName( mergeServiceAccountName( baseSpec.getServiceAccountName(), podData.getSpec().getServiceAccountName())); baseSpec.getAdditionalProperties().putAll(podData.getSpec().getAdditionalProperties()); } Map<String, String> matchLabels = new HashMap<>(); matchLabels.put(DEPLOYMENT_NAME_LABEL, baseDeployment.getMetadata().getName()); putLabels(basePodMeta, matchLabels); baseDeployment.getSpec().getSelector().setMatchLabels(matchLabels); return baseDeployment; }  Deployment merge(List<PodData> podsData); static final String DEPLOYMENT_NAME_LABEL; }
@Test public void shouldMergeMetasOfPodsData() throws Exception { ObjectMeta podMeta1 = new ObjectMetaBuilder() .withName("ignored-1") .withAnnotations(ImmutableMap.of("ann1", "v1")) .withLabels(ImmutableMap.of("label1", "v1")) .build(); podMeta1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(new PodSpecBuilder().build(), podMeta1); ObjectMeta podMeta2 = new ObjectMetaBuilder() .withName("ignored-2") .withAnnotations(ImmutableMap.of("ann2", "v2")) .withLabels(ImmutableMap.of("label2", "v2")) .build(); podMeta2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(new PodSpecBuilder().build(), podMeta2); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); ObjectMeta podMeta = podTemplate.getMetadata(); verifyContainsAllFrom(podMeta, podData1.getMetadata()); verifyContainsAllFrom(podMeta, podData2.getMetadata()); }
@Test public void shouldMatchMergedPodTemplateLabelsWithDeploymentSelector() throws Exception { ObjectMeta podMeta1 = new ObjectMetaBuilder() .withName("ignored-1") .withAnnotations(ImmutableMap.of("ann1", "v1")) .withLabels(ImmutableMap.of("label1", "v1")) .build(); podMeta1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(new PodSpecBuilder().build(), podMeta1); Deployment merged = podMerger.merge(Collections.singletonList(podData1)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); ObjectMeta podMeta = podTemplate.getMetadata(); Map<String, String> deploymentSelector = merged.getSpec().getSelector().getMatchLabels(); assertTrue(podMeta.getLabels().entrySet().containsAll(deploymentSelector.entrySet())); }
@Test public void shouldMergeSpecsOfPodsData() throws Exception { PodSpec podSpec1 = new PodSpecBuilder() .withContainers(new ContainerBuilder().withName("c1").build()) .withInitContainers(new ContainerBuilder().withName("initC1").build()) .withVolumes(new VolumeBuilder().withName("v1").build()) .withImagePullSecrets(new LocalObjectReferenceBuilder().withName("secret1").build()) .build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder() .withContainers(new ContainerBuilder().withName("c2").build()) .withInitContainers(new ContainerBuilder().withName("initC2").build()) .withVolumes(new VolumeBuilder().withName("v2").build()) .withImagePullSecrets(new LocalObjectReferenceBuilder().withName("secret2").build()) .build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); verifyContainsAllFrom(podTemplate.getSpec(), podData1.getSpec()); verifyContainsAllFrom(podTemplate.getSpec(), podData2.getSpec()); }
@Test public void shouldGenerateContainerNamesIfCollisionHappened() throws Exception { PodSpec podSpec1 = new PodSpecBuilder().withContainers(new ContainerBuilder().withName("c").build()).build(); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder().withContainers(new ContainerBuilder().withName("c").build()).build(); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); List<Container> containers = podTemplate.getSpec().getContainers(); assertEquals(containers.size(), 2); Container container1 = containers.get(0); assertEquals(container1.getName(), "c"); Container container2 = containers.get(1); assertNotEquals(container2.getName(), "c"); assertTrue(container2.getName().startsWith("c")); }
@Test public void shouldGenerateInitContainerNamesIfCollisionHappened() throws Exception { PodSpec podSpec1 = new PodSpecBuilder() .withInitContainers(new ContainerBuilder().withName("initC").build()) .build(); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder() .withInitContainers(new ContainerBuilder().withName("initC").build()) .build(); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); List<Container> initContainers = podTemplate.getSpec().getInitContainers(); assertEquals(initContainers.size(), 2); Container container1 = initContainers.get(0); assertEquals(container1.getName(), "initC"); Container container2 = initContainers.get(1); assertNotEquals(container2.getName(), "initC"); assertTrue(container2.getName().startsWith("initC")); }
@Test( expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "Pods have to have volumes with unique names but there are multiple `volume` volumes") public void shouldThrownAnExceptionIfVolumeNameCollisionHappened() throws Exception { PodSpec podSpec1 = new PodSpecBuilder().withVolumes(new VolumeBuilder().withName("volume").build()).build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder().withVolumes(new VolumeBuilder().withName("volume").build()).build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); podMerger.merge(Arrays.asList(podData1, podData2)); }
@Test public void shouldMergeProjectVolumesWithoutException() throws Exception { PodSpec podSpec1 = new PodSpecBuilder() .withVolumes(new VolumeBuilder().withName(PROJECTS_VOLUME_NAME).build()) .build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder() .withVolumes(new VolumeBuilder().withName(PROJECTS_VOLUME_NAME).build()) .build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); assertEquals(podTemplate.getSpec().getVolumes().size(), 1); }
@Test public void shouldNotAddImagePullPolicyTwice() throws Exception { PodSpec podSpec1 = new PodSpecBuilder() .withImagePullSecrets(new LocalObjectReferenceBuilder().withName("secret").build()) .build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder() .withImagePullSecrets(new LocalObjectReferenceBuilder().withName("secret").build()) .build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); List<LocalObjectReference> imagePullSecrets = podTemplate.getSpec().getImagePullSecrets(); assertEquals(imagePullSecrets.size(), 1); assertEquals(imagePullSecrets.get(0).getName(), "secret"); }
@Test(dataProvider = "terminationGracePeriodProvider") public void shouldBeAbleToMergeTerminationGracePeriodS( List<Long> terminationGracePeriods, Long expectedResultLong) throws ValidationException { List<PodData> podData = terminationGracePeriods .stream() .map( p -> new PodData( new PodSpecBuilder().withTerminationGracePeriodSeconds(p).build(), new ObjectMetaBuilder().build())) .collect(Collectors.toList()); Deployment merged = podMerger.merge(podData); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); assertEquals(podTemplate.getSpec().getTerminationGracePeriodSeconds(), expectedResultLong); }
@Test public void shouldAssignSecurityContextSharedByPods() throws Exception { PodSpec podSpec1 = new PodSpecBuilder() .withSecurityContext(new PodSecurityContextBuilder().withRunAsUser(42L).build()) .build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder() .withSecurityContext(new PodSecurityContextBuilder().withRunAsUser(42L).build()) .build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); PodSecurityContext sc = podTemplate.getSpec().getSecurityContext(); assertEquals(sc.getRunAsUser(), (Long) 42L); }
@Test(expectedExceptions = ValidationException.class) public void shouldFailIfSecurityContextDiffersInPods() throws Exception { PodSpec podSpec1 = new PodSpecBuilder() .withSecurityContext(new PodSecurityContextBuilder().withRunAsUser(42L).build()) .build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder() .withSecurityContext(new PodSecurityContextBuilder().withRunAsUser(43L).build()) .build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); }
@Test public void shouldAssignServiceAccountSharedByPods() throws Exception { PodSpec podSpec1 = new PodSpecBuilder().withServiceAccount("sa").build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder().withServiceAccount("sa").build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); String sa = podTemplate.getSpec().getServiceAccount(); assertEquals(sa, "sa"); }
@Test(expectedExceptions = ValidationException.class) public void shouldFailServiceAccountDiffersInPods() throws Exception { PodSpec podSpec1 = new PodSpecBuilder().withServiceAccount("sa").build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder().withServiceAccount("sb").build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); }
@Test public void shouldAssignServiceAccountNameSharedByPods() throws Exception { PodSpec podSpec1 = new PodSpecBuilder().withServiceAccountName("sa").build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder().withServiceAccountName("sa").build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); PodTemplateSpec podTemplate = merged.getSpec().getTemplate(); String sa = podTemplate.getSpec().getServiceAccountName(); assertEquals(sa, "sa"); }
@Test(expectedExceptions = ValidationException.class) public void shouldFailServiceAccountNameDiffersInPods() throws Exception { PodSpec podSpec1 = new PodSpecBuilder().withServiceAccountName("sa").build(); podSpec1.setAdditionalProperty("add1", 1L); PodData podData1 = new PodData(podSpec1, new ObjectMetaBuilder().build()); PodSpec podSpec2 = new PodSpecBuilder().withServiceAccountName("sb").build(); podSpec2.setAdditionalProperty("add2", 2L); PodData podData2 = new PodData(podSpec2, new ObjectMetaBuilder().build()); Deployment merged = podMerger.merge(Arrays.asList(podData1, podData2)); }