use of io.kubernetes.client.openapi.models.V1VolumeMount in project heron by twitter.
the class V1Controller method createStatefulSet.
/**
* Creates and configures the <code>StatefulSet</code> which the topology's <code>executor</code>s will run in.
* @param containerResource Passed down to configure the <code>executor</code> resource limits.
* @param numberOfInstances Used to configure the execution command and ports for the <code>executor</code>.
* @param isExecutor Flag used to configure components specific to <code>executor</code> and <code>manager</code>.
* @return A fully configured <code>StatefulSet</code> for the topology's <code>executors</code>.
*/
private V1StatefulSet createStatefulSet(Resource containerResource, int numberOfInstances, boolean isExecutor) {
final String topologyName = getTopologyName();
final Config runtimeConfiguration = getRuntimeConfiguration();
final List<V1Volume> volumes = new LinkedList<>();
final List<V1VolumeMount> volumeMounts = new LinkedList<>();
// Collect Persistent Volume Claim configurations from the CLI.
final Map<String, Map<KubernetesConstants.VolumeConfigKeys, String>> configsPVC = KubernetesContext.getVolumeClaimTemplates(getConfiguration(), isExecutor);
// Collect all Volume configurations from the CLI and generate Volumes and Volume Mounts.
createVolumeAndMountsPersistentVolumeClaimCLI(configsPVC, volumes, volumeMounts);
createVolumeAndMountsHostPathCLI(KubernetesContext.getVolumeHostPath(getConfiguration(), isExecutor), volumes, volumeMounts);
createVolumeAndMountsEmptyDirCLI(KubernetesContext.getVolumeEmptyDir(getConfiguration(), isExecutor), volumes, volumeMounts);
createVolumeAndMountsNFSCLI(KubernetesContext.getVolumeNFS(getConfiguration(), isExecutor), volumes, volumeMounts);
final V1StatefulSet statefulSet = new V1StatefulSet();
// Setup StatefulSet's metadata.
final V1ObjectMeta objectMeta = new V1ObjectMeta().name(getStatefulSetName(isExecutor)).labels(getPodLabels(topologyName));
statefulSet.setMetadata(objectMeta);
// Create the StatefulSet Spec.
// Reduce replica count by one for Executors and set to one for Manager.
final int replicasCount = isExecutor ? Runtime.numContainers(runtimeConfiguration).intValue() - 1 : 1;
final V1StatefulSetSpec statefulSetSpec = new V1StatefulSetSpec().serviceName(topologyName).replicas(replicasCount);
// Parallel pod management tells the StatefulSet controller to launch or terminate
// all Pods in parallel, and not to wait for Pods to become Running and Ready or completely
// terminated prior to launching or terminating another Pod.
statefulSetSpec.setPodManagementPolicy("Parallel");
// Add selector match labels "app=heron" and "topology=topology-name"
// so we know which pods to manage.
final V1LabelSelector selector = new V1LabelSelector().matchLabels(getPodMatchLabels(topologyName));
statefulSetSpec.setSelector(selector);
// Create a Pod Template.
final V1PodTemplateSpec podTemplateSpec = loadPodFromTemplate(isExecutor);
// Set up Pod Metadata.
final V1ObjectMeta templateMetaData = new V1ObjectMeta().labels(getPodLabels(topologyName));
Map<String, String> annotations = new HashMap<>();
annotations.putAll(getPodAnnotations());
annotations.putAll(getPrometheusAnnotations());
templateMetaData.setAnnotations(annotations);
podTemplateSpec.setMetadata(templateMetaData);
configurePodSpec(podTemplateSpec, containerResource, numberOfInstances, isExecutor, volumes, volumeMounts);
statefulSetSpec.setTemplate(podTemplateSpec);
statefulSet.setSpec(statefulSetSpec);
statefulSetSpec.setVolumeClaimTemplates(createPersistentVolumeClaims(configsPVC));
return statefulSet;
}
use of io.kubernetes.client.openapi.models.V1VolumeMount in project heron by twitter.
the class V1Controller method mountVolumeIfPresent.
/**
* Adds volume mounts to the <code>container</code> that Heron requires. Heron's values taking precedence.
* @param container <code>container</code> to be configured.
*/
@VisibleForTesting
protected void mountVolumeIfPresent(final V1Container container) {
final Config config = getConfiguration();
if (KubernetesContext.hasContainerVolume(config)) {
final V1VolumeMount mount = new V1VolumeMount().name(KubernetesContext.getContainerVolumeName(config)).mountPath(KubernetesContext.getContainerVolumeMountPath(config));
// Merge volume mounts. Deduplicate using mount's name with Heron defaults taking precedence.
KubernetesUtils.V1ControllerUtils<V1VolumeMount> utils = new KubernetesUtils.V1ControllerUtils<>();
container.setVolumeMounts(utils.mergeListsDedupe(Collections.singletonList(mount), container.getVolumeMounts(), Comparator.comparing(V1VolumeMount::getName), "Pod Template Volume Mounts"));
}
}
use of io.kubernetes.client.openapi.models.V1VolumeMount in project heron by twitter.
the class V1ControllerTest method testCreateVolumeAndMountsHostPathCLI.
@Test
public void testCreateVolumeAndMountsHostPathCLI() {
final String volumeName = "volume-name-host-path";
final String type = "DirectoryOrCreate";
final String pathOnHost = "path.on.host";
final String path = "/path/to/mount";
final String subPath = "/sub/path/to/mount";
// Host Path.
final Map<String, Map<VolumeConfigKeys, String>> config = ImmutableMap.of(volumeName, new HashMap<VolumeConfigKeys, String>() {
{
put(VolumeConfigKeys.type, type);
put(VolumeConfigKeys.pathOnHost, pathOnHost);
put(VolumeConfigKeys.path, path);
put(VolumeConfigKeys.subPath, subPath);
}
});
final List<V1Volume> expectedVolumes = Collections.singletonList(new V1VolumeBuilder().withName(volumeName).withNewHostPath().withNewType(type).withNewPath(pathOnHost).endHostPath().build());
final List<V1VolumeMount> expectedMounts = Collections.singletonList(new V1VolumeMountBuilder().withName(volumeName).withMountPath(path).withSubPath(subPath).build());
List<V1Volume> actualVolumes = new LinkedList<>();
List<V1VolumeMount> actualMounts = new LinkedList<>();
v1ControllerPodTemplate.createVolumeAndMountsHostPathCLI(config, actualVolumes, actualMounts);
Assert.assertEquals("Host Path Volume populated", expectedVolumes, actualVolumes);
Assert.assertEquals("Host Path Volume Mount populated", expectedMounts, actualMounts);
}
use of io.kubernetes.client.openapi.models.V1VolumeMount in project heron by twitter.
the class V1ControllerTest method testConfigurePodWithVolumesAndMountsFromCLI.
@Test
public void testConfigurePodWithVolumesAndMountsFromCLI() {
final String volumeNameClashing = "clashing-volume";
final String volumeMountNameClashing = "original-volume-mount";
V1Volume baseVolume = new V1VolumeBuilder().withName(volumeNameClashing).withNewPersistentVolumeClaim().withClaimName("Original Base Claim Name").endPersistentVolumeClaim().build();
V1VolumeMount baseVolumeMount = new V1VolumeMountBuilder().withName(volumeMountNameClashing).withMountPath("/original/mount/path").build();
V1Volume clashingVolume = new V1VolumeBuilder().withName(volumeNameClashing).withNewPersistentVolumeClaim().withClaimName("Clashing Claim Replaced").endPersistentVolumeClaim().build();
V1VolumeMount clashingVolumeMount = new V1VolumeMountBuilder().withName(volumeMountNameClashing).withMountPath("/clashing/mount/path").build();
V1Volume secondaryVolume = new V1VolumeBuilder().withName("secondary-volume").withNewPersistentVolumeClaim().withClaimName("Original Secondary Claim Name").endPersistentVolumeClaim().build();
V1VolumeMount secondaryVolumeMount = new V1VolumeMountBuilder().withName("secondary-volume-mount").withMountPath("/secondary/mount/path").build();
// Test case container.
// Input: [0] Pod Spec to modify, [1] Heron container to modify, [2] List of Volumes
// [3] List of Volume Mounts.
// Output: The expected <V1PodSpec> and <V1Container>.
final List<TestTuple<Object[], Pair<V1PodSpec, V1Container>>> testCases = new LinkedList<>();
// No Persistent Volume Claim.
final V1PodSpec podSpecEmptyCase = new V1PodSpecBuilder().withVolumes(baseVolume).build();
final V1Container executorEmptyCase = new V1ContainerBuilder().withVolumeMounts(baseVolumeMount).build();
final V1PodSpec expectedEmptyPodSpec = new V1PodSpecBuilder().withVolumes(baseVolume).build();
final V1Container expectedEmptyExecutor = new V1ContainerBuilder().withVolumeMounts(baseVolumeMount).build();
testCases.add(new TestTuple<>("Empty", new Object[] { podSpecEmptyCase, executorEmptyCase, new LinkedList<>(), new LinkedList<>() }, new Pair<>(expectedEmptyPodSpec, expectedEmptyExecutor)));
// Non-clashing Persistent Volume Claim.
final V1PodSpec podSpecNoClashCase = new V1PodSpecBuilder().withVolumes(baseVolume).build();
final V1Container executorNoClashCase = new V1ContainerBuilder().withVolumeMounts(baseVolumeMount).build();
final V1PodSpec expectedNoClashPodSpec = new V1PodSpecBuilder().addToVolumes(baseVolume).addToVolumes(secondaryVolume).build();
final V1Container expectedNoClashExecutor = new V1ContainerBuilder().addToVolumeMounts(baseVolumeMount).addToVolumeMounts(secondaryVolumeMount).build();
testCases.add(new TestTuple<>("No Clash", new Object[] { podSpecNoClashCase, executorNoClashCase, Collections.singletonList(secondaryVolume), Collections.singletonList(secondaryVolumeMount) }, new Pair<>(expectedNoClashPodSpec, expectedNoClashExecutor)));
// Clashing Persistent Volume Claim.
final V1PodSpec podSpecClashCase = new V1PodSpecBuilder().withVolumes(baseVolume).build();
final V1Container executorClashCase = new V1ContainerBuilder().withVolumeMounts(baseVolumeMount).build();
final V1PodSpec expectedClashPodSpec = new V1PodSpecBuilder().addToVolumes(clashingVolume).addToVolumes(secondaryVolume).build();
final V1Container expectedClashExecutor = new V1ContainerBuilder().addToVolumeMounts(clashingVolumeMount).addToVolumeMounts(secondaryVolumeMount).build();
testCases.add(new TestTuple<>("Clashing", new Object[] { podSpecClashCase, executorClashCase, Arrays.asList(clashingVolume, secondaryVolume), Arrays.asList(clashingVolumeMount, secondaryVolumeMount) }, new Pair<>(expectedClashPodSpec, expectedClashExecutor)));
// Testing loop.
for (TestTuple<Object[], Pair<V1PodSpec, V1Container>> testCase : testCases) {
v1ControllerWithPodTemplate.configurePodWithVolumesAndMountsFromCLI((V1PodSpec) testCase.input[0], (V1Container) testCase.input[1], (List<V1Volume>) testCase.input[2], (List<V1VolumeMount>) testCase.input[3]);
Assert.assertEquals("Pod Specs match " + testCase.description, testCase.input[0], testCase.expected.first);
Assert.assertEquals("Executors match " + testCase.description, testCase.input[1], testCase.expected.second);
}
}
use of io.kubernetes.client.openapi.models.V1VolumeMount in project heron by twitter.
the class V1ControllerTest method testMountVolumeIfPresent.
@Test
public void testMountVolumeIfPresent() {
final String pathDefault = "config-host-volume-path";
final String pathNameDefault = "config-host-volume-name";
final Config configWithVolumes = Config.newBuilder().put(KubernetesContext.KUBERNETES_CONTAINER_VOLUME_MOUNT_NAME, pathNameDefault).put(KubernetesContext.KUBERNETES_CONTAINER_VOLUME_MOUNT_PATH, pathDefault).build();
final V1Controller controllerWithMounts = new V1Controller(configWithVolumes, RUNTIME);
final V1VolumeMount volumeDefault = new V1VolumeMountBuilder().withName(pathNameDefault).withMountPath(pathDefault).build();
final V1VolumeMount volumeCustom = new V1VolumeMountBuilder().withName("custom-volume-mount").withMountPath("should-be-kept").build();
final List<V1VolumeMount> expectedMountsDefault = Collections.singletonList(volumeDefault);
final List<V1VolumeMount> expectedMountsCustom = Arrays.asList(volumeCustom, volumeDefault);
final List<V1VolumeMount> volumeMountsCustomList = Arrays.asList(new V1VolumeMountBuilder().withName(pathNameDefault).withMountPath("should-be-replaced").build(), volumeCustom);
// No Volume Mounts set.
V1Controller controllerDoNotSetMounts = new V1Controller(Config.newBuilder().build(), RUNTIME);
V1Container containerNoSetMounts = new V1Container();
controllerDoNotSetMounts.mountVolumeIfPresent(containerNoSetMounts);
Assert.assertNull(containerNoSetMounts.getVolumeMounts());
// Default. Null Volume Mounts.
V1Container containerNull = new V1ContainerBuilder().build();
controllerWithMounts.mountVolumeIfPresent(containerNull);
Assert.assertTrue("Default VOLUME MOUNTS should be set in container with null VOLUME MOUNTS", CollectionUtils.containsAll(expectedMountsDefault, containerNull.getVolumeMounts()));
// Empty Volume Mounts.
V1Container containerEmpty = new V1ContainerBuilder().withVolumeMounts(new LinkedList<>()).build();
controllerWithMounts.mountVolumeIfPresent(containerEmpty);
Assert.assertTrue("Default VOLUME MOUNTS should be set in container with empty VOLUME MOUNTS", CollectionUtils.containsAll(expectedMountsDefault, containerEmpty.getVolumeMounts()));
// Custom Volume Mounts.
V1Container containerCustom = new V1ContainerBuilder().withVolumeMounts(volumeMountsCustomList).build();
controllerWithMounts.mountVolumeIfPresent(containerCustom);
Assert.assertTrue("Default VOLUME MOUNTS should be set in container with custom VOLUME MOUNTS", CollectionUtils.containsAll(expectedMountsCustom, containerCustom.getVolumeMounts()));
}
Aggregations