Search in sources :

Example 6 with TopologySubmissionException

use of org.apache.heron.scheduler.TopologySubmissionException in project heron by twitter.

the class KubernetesContextTest method testGetVolumeConfigsErrors.

@Test
public void testGetVolumeConfigsErrors() {
    final String prefix = KubernetesContext.KUBERNETES_VOLUME_CLAIM_PREFIX;
    final String volumeNameValid = "volume-name-valid";
    final String volumeNameInvalid = "volume-Name-Invalid";
    final String passingValue = "should-pass";
    final String failureValue = "Should-Fail";
    final String generalFailureMessage = "Invalid Volume configuration";
    final String keyPattern = String.format(KubernetesContext.KUBERNETES_VOLUME_CLAIM_PREFIX + "%%s.%%s", KubernetesConstants.EXECUTOR_NAME);
    final List<TestTuple<Config, String>> testCases = new LinkedList<>();
    // Invalid option key test.
    final Config configInvalidOption = Config.newBuilder().put(String.format(keyPattern, volumeNameValid, "claimName"), passingValue).put(String.format(keyPattern, volumeNameValid, "storageClassName"), passingValue).put(String.format(keyPattern, volumeNameValid, "sizeLimit"), passingValue).put(String.format(keyPattern, volumeNameValid, "accessModes"), passingValue).put(String.format(keyPattern, volumeNameValid, "volumeMode"), passingValue).put(String.format(keyPattern, volumeNameValid, "path"), passingValue).put(String.format(keyPattern, volumeNameValid, "subPath"), passingValue).put(String.format(keyPattern, volumeNameValid, "server"), passingValue).put(String.format(keyPattern, volumeNameValid, "readOnly"), passingValue).put(String.format(keyPattern, volumeNameValid, "type"), passingValue).put(String.format(keyPattern, volumeNameValid, "medium"), passingValue).put(String.format(keyPattern, volumeNameValid, "NonExistentKey"), failureValue).build();
    testCases.add(new TestTuple<>("Invalid option key should trigger exception", configInvalidOption, generalFailureMessage));
    // Invalid Volume Name.
    final Config configInvalidVolumeName = Config.newBuilder().put(String.format(keyPattern, volumeNameInvalid, "path"), failureValue).build();
    testCases.add(new TestTuple<>("Invalid Volume Name should trigger exception", configInvalidVolumeName, "lowercase RFC-1123"));
    // Required Path.
    final Config configRequiredPath = Config.newBuilder().put(String.format(keyPattern, volumeNameValid, "claimName"), passingValue).put(String.format(keyPattern, volumeNameValid, "storageClassName"), passingValue).put(String.format(keyPattern, volumeNameValid, "sizeLimit"), passingValue).put(String.format(keyPattern, volumeNameValid, "accessModes"), passingValue).put(String.format(keyPattern, volumeNameValid, "volumeMode"), passingValue).put(String.format(keyPattern, volumeNameValid, "subPath"), passingValue).put(String.format(keyPattern, volumeNameValid, "server"), passingValue).put(String.format(keyPattern, volumeNameValid, "readOnly"), passingValue).put(String.format(keyPattern, volumeNameValid, "type"), passingValue).put(String.format(keyPattern, volumeNameValid, "medium"), passingValue).build();
    testCases.add(new TestTuple<>("Missing path should trigger exception", configRequiredPath, "All Volumes require a 'path'."));
    // Disabled.
    final Config configDisabled = Config.newBuilder().put(KubernetesContext.KUBERNETES_VOLUME_FROM_CLI_DISABLED, "true").put(String.format(keyPattern, volumeNameValid, "claimName"), passingValue).put(String.format(keyPattern, volumeNameValid, "storageClassName"), passingValue).put(String.format(keyPattern, volumeNameValid, "sizeLimit"), passingValue).put(String.format(keyPattern, volumeNameValid, "accessModes"), passingValue).put(String.format(keyPattern, volumeNameValid, "volumeMode"), passingValue).put(String.format(keyPattern, volumeNameValid, "path"), passingValue).put(String.format(keyPattern, volumeNameValid, "subPath"), passingValue).put(String.format(keyPattern, volumeNameValid, "server"), passingValue).put(String.format(keyPattern, volumeNameValid, "readOnly"), passingValue).put(String.format(keyPattern, volumeNameValid, "type"), passingValue).put(String.format(keyPattern, volumeNameValid, "medium"), passingValue).build();
    testCases.add(new TestTuple<>("Disabled functionality should trigger exception", configDisabled, "Configuring Volumes from the CLI is disabled."));
    // Testing loop.
    for (TestTuple<Config, String> testCase : testCases) {
        String message = "";
        try {
            KubernetesContext.getVolumeConfigs(testCase.input, prefix, true);
        } catch (TopologySubmissionException e) {
            message = e.getMessage();
        }
        Assert.assertTrue(testCase.description, message.contains(testCase.expected));
    }
}
Also used : TopologySubmissionException(org.apache.heron.scheduler.TopologySubmissionException) TestTuple(org.apache.heron.scheduler.kubernetes.KubernetesUtils.TestTuple) Config(org.apache.heron.spi.common.Config) LinkedList(java.util.LinkedList) Test(org.junit.Test)

Example 7 with TopologySubmissionException

use of org.apache.heron.scheduler.TopologySubmissionException in project heron by twitter.

the class V1ControllerTest method testLoadPodFromTemplateNoTargetConfigMap.

@Test
public void testLoadPodFromTemplateNoTargetConfigMap() {
    final List<TestTuple<Boolean, String>> testCases = new LinkedList<>();
    testCases.add(new TestTuple<>("Executor no target ConfigMap", true, "Failed to locate Pod Template"));
    testCases.add(new TestTuple<>("Manager no target ConfigMap", false, "Failed to locate Pod Template"));
    final V1ConfigMap configMapNoTargetData = new V1ConfigMapBuilder().withNewMetadata().withName(CONFIGMAP_NAME).endMetadata().addToData("Dummy Key", "Dummy Value").build();
    for (TestTuple<Boolean, String> testCase : testCases) {
        doReturn(configMapNoTargetData).when(v1ControllerWithPodTemplate).getConfigMap(anyString());
        String message = "";
        try {
            v1ControllerWithPodTemplate.loadPodFromTemplate(testCase.input);
        } catch (TopologySubmissionException e) {
            message = e.getMessage();
        }
        Assert.assertTrue(testCase.description, message.contains(testCase.expected));
    }
}
Also used : V1ConfigMapBuilder(io.kubernetes.client.openapi.models.V1ConfigMapBuilder) TopologySubmissionException(org.apache.heron.scheduler.TopologySubmissionException) TestTuple(org.apache.heron.scheduler.kubernetes.KubernetesUtils.TestTuple) Matchers.anyString(org.mockito.Matchers.anyString) LinkedList(java.util.LinkedList) V1ConfigMap(io.kubernetes.client.openapi.models.V1ConfigMap) Test(org.junit.Test)

Example 8 with TopologySubmissionException

use of org.apache.heron.scheduler.TopologySubmissionException in project heron by twitter.

the class V1ControllerTest method testLoadPodFromTemplateInvalidConfigMap.

@Test
public void testLoadPodFromTemplateInvalidConfigMap() {
    // ConfigMap with an invalid Pod Template.
    final String invalidPodTemplate = "apiVersion: apps/v1\n" + "kind: InvalidTemplate\n" + "metadata:\n" + "  name: heron-tracker\n" + "  namespace: default\n" + "template:\n" + "  metadata:\n" + "    labels:\n" + "      app: heron-tracker\n" + "  spec:\n";
    final V1ConfigMap configMap = new V1ConfigMapBuilder().withNewMetadata().withName(CONFIGMAP_NAME).endMetadata().addToData(POD_TEMPLATE_NAME, invalidPodTemplate).build();
    // Test case container.
    // Input: ConfigMap to setup mock V1Controller, Boolean flag for executor/manager switch.
    // Output: The expected Pod template as a string.
    final List<TestTuple<Pair<V1ConfigMap, Boolean>, String>> testCases = new LinkedList<>();
    testCases.add(new TestTuple<>("Executor invalid Pod Template", new Pair<>(configMap, true), "Error parsing"));
    testCases.add(new TestTuple<>("Manager invalid Pod Template", new Pair<>(configMap, false), "Error parsing"));
    // Test loop.
    for (TestTuple<Pair<V1ConfigMap, Boolean>, String> testCase : testCases) {
        doReturn(testCase.input.first).when(v1ControllerWithPodTemplate).getConfigMap(anyString());
        String message = "";
        try {
            v1ControllerWithPodTemplate.loadPodFromTemplate(testCase.input.second);
        } catch (TopologySubmissionException e) {
            message = e.getMessage();
        }
        Assert.assertTrue(message.contains(testCase.expected));
    }
}
Also used : V1ConfigMapBuilder(io.kubernetes.client.openapi.models.V1ConfigMapBuilder) TopologySubmissionException(org.apache.heron.scheduler.TopologySubmissionException) TestTuple(org.apache.heron.scheduler.kubernetes.KubernetesUtils.TestTuple) Matchers.anyString(org.mockito.Matchers.anyString) V1ConfigMap(io.kubernetes.client.openapi.models.V1ConfigMap) LinkedList(java.util.LinkedList) Pair(org.apache.heron.common.basics.Pair) Test(org.junit.Test)

Example 9 with TopologySubmissionException

use of org.apache.heron.scheduler.TopologySubmissionException in project heron by twitter.

the class KubernetesUtilsTest method testMergeListsDedupe.

@Test
public void testMergeListsDedupe() {
    final String description = "Pod Template Environment Variables";
    final List<V1EnvVar> heronEnvVars = Collections.unmodifiableList(V1Controller.getExecutorEnvVars());
    final V1EnvVar additionEnvVar = new V1EnvVar().name("env-variable-to-be-kept").valueFrom(new V1EnvVarSource().fieldRef(new V1ObjectFieldSelector().fieldPath("env-variable-was-kept")));
    final List<V1EnvVar> expectedEnvVars = Collections.unmodifiableList(new LinkedList<V1EnvVar>(V1Controller.getExecutorEnvVars()) {

        {
            add(additionEnvVar);
        }
    });
    final List<V1EnvVar> inputEnvVars = Arrays.asList(new V1EnvVar().name(KubernetesConstants.ENV_HOST).valueFrom(new V1EnvVarSource().fieldRef(new V1ObjectFieldSelector().fieldPath("env-host-to-be-replaced"))), new V1EnvVar().name(KubernetesConstants.ENV_POD_NAME).valueFrom(new V1EnvVarSource().fieldRef(new V1ObjectFieldSelector().fieldPath("pod-name-to-be-replaced"))), additionEnvVar);
    KubernetesUtils.V1ControllerUtils<V1EnvVar> v1ControllerUtils = new KubernetesUtils.V1ControllerUtils<>();
    // Both input lists are null.
    Assert.assertNull("Both input lists are <null>", v1ControllerUtils.mergeListsDedupe(null, null, Comparator.comparing(V1EnvVar::getName), description));
    // <primaryList> is <null>.
    Assert.assertEquals("<primaryList> is null and <secondaryList> should be returned", inputEnvVars, v1ControllerUtils.mergeListsDedupe(null, inputEnvVars, Comparator.comparing(V1EnvVar::getName), description));
    // <primaryList> is empty.
    Assert.assertEquals("<primaryList> is empty and <secondaryList> should be returned", inputEnvVars, v1ControllerUtils.mergeListsDedupe(new LinkedList<>(), inputEnvVars, Comparator.comparing(V1EnvVar::getName), description));
    // <secondaryList> is <null>.
    Assert.assertEquals("<secondaryList> is null and <primaryList> should be returned", heronEnvVars, v1ControllerUtils.mergeListsDedupe(heronEnvVars, null, Comparator.comparing(V1EnvVar::getName), description));
    // <secondaryList> is empty.
    Assert.assertEquals("<secondaryList> is empty and <primaryList> should be returned", heronEnvVars, v1ControllerUtils.mergeListsDedupe(heronEnvVars, new LinkedList<>(), Comparator.comparing(V1EnvVar::getName), description));
    // Merge both lists.
    Assert.assertTrue("<primaryList> and <secondaryList> merged and deduplicated", expectedEnvVars.containsAll(v1ControllerUtils.mergeListsDedupe(heronEnvVars, inputEnvVars, Comparator.comparing(V1EnvVar::getName), description)));
    // Expect thrown error.
    String errorMessage = "";
    try {
        v1ControllerUtils.mergeListsDedupe(heronEnvVars, Collections.singletonList(new V1EnvVar()), Comparator.comparing(V1EnvVar::getName), description);
    } catch (TopologySubmissionException e) {
        errorMessage = e.getMessage();
    }
    Assert.assertTrue("Expecting error to be thrown for null deduplication key", errorMessage.contains(description));
}
Also used : TopologySubmissionException(org.apache.heron.scheduler.TopologySubmissionException) V1EnvVarSource(io.kubernetes.client.openapi.models.V1EnvVarSource) V1ObjectFieldSelector(io.kubernetes.client.openapi.models.V1ObjectFieldSelector) V1EnvVar(io.kubernetes.client.openapi.models.V1EnvVar) LinkedList(java.util.LinkedList) Test(org.junit.Test)

Example 10 with TopologySubmissionException

use of org.apache.heron.scheduler.TopologySubmissionException in project heron by twitter.

the class V1Controller method loadPodFromTemplate.

/**
 * Initiates the process of locating and loading <code>Pod Template</code> from a <code>ConfigMap</code>.
 * The loaded text is then parsed into a usable <code>Pod Template</code>.
 * @param isExecutor Flag to indicate loading of <code>Pod Template</code> for <code>Executor</code>
 *                   or <code>Manager</code>.
 * @return A <code>Pod Template</code> which is loaded and parsed from a <code>ConfigMap</code>.
 */
@VisibleForTesting
protected V1PodTemplateSpec loadPodFromTemplate(boolean isExecutor) {
    final Pair<String, String> podTemplateConfigMapName = getPodTemplateLocation(isExecutor);
    // Default Pod Template.
    if (podTemplateConfigMapName == null) {
        LOG.log(Level.INFO, "Configuring cluster with the Default Pod Template");
        return new V1PodTemplateSpec();
    }
    if (isPodTemplateDisabled) {
        throw new TopologySubmissionException("Custom Pod Templates are disabled");
    }
    final String configMapName = podTemplateConfigMapName.first;
    final String podTemplateName = podTemplateConfigMapName.second;
    // Attempt to locate ConfigMap with provided Pod Template name.
    try {
        V1ConfigMap configMap = getConfigMap(configMapName);
        if (configMap == null) {
            throw new ApiException(String.format("K8s client unable to locate ConfigMap '%s'", configMapName));
        }
        final Map<String, String> configMapData = configMap.getData();
        if (configMapData != null && configMapData.containsKey(podTemplateName)) {
            // NullPointerException when Pod Template is empty.
            V1PodTemplateSpec podTemplate = ((V1PodTemplate) Yaml.load(configMapData.get(podTemplateName))).getTemplate();
            LOG.log(Level.INFO, String.format("Configuring cluster with the %s.%s Pod Template", configMapName, podTemplateName));
            return podTemplate;
        }
        // Failure to locate Pod Template with provided name.
        throw new ApiException(String.format("Failed to locate Pod Template '%s' in ConfigMap '%s'", podTemplateName, configMapName));
    } catch (ApiException e) {
        KubernetesUtils.logExceptionWithDetails(LOG, e.getMessage(), e);
        throw new TopologySubmissionException(e.getMessage());
    } catch (IOException | ClassCastException | NullPointerException e) {
        final String message = String.format("Error parsing Pod Template '%s' in ConfigMap '%s'", podTemplateName, configMapName);
        KubernetesUtils.logExceptionWithDetails(LOG, message, e);
        throw new TopologySubmissionException(message);
    }
}
Also used : TopologySubmissionException(org.apache.heron.scheduler.TopologySubmissionException) V1PodTemplate(io.kubernetes.client.openapi.models.V1PodTemplate) V1PodTemplateSpec(io.kubernetes.client.openapi.models.V1PodTemplateSpec) IOException(java.io.IOException) V1ConfigMap(io.kubernetes.client.openapi.models.V1ConfigMap) ApiException(io.kubernetes.client.openapi.ApiException) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Aggregations

TopologySubmissionException (org.apache.heron.scheduler.TopologySubmissionException)14 Test (org.junit.Test)8 LinkedList (java.util.LinkedList)7 V1ConfigMap (io.kubernetes.client.openapi.models.V1ConfigMap)6 TestTuple (org.apache.heron.scheduler.kubernetes.KubernetesUtils.TestTuple)6 Matchers.anyString (org.mockito.Matchers.anyString)6 V1ConfigMapBuilder (io.kubernetes.client.openapi.models.V1ConfigMapBuilder)4 VisibleForTesting (com.google.common.annotations.VisibleForTesting)3 Pair (org.apache.heron.common.basics.Pair)3 ApiException (io.kubernetes.client.openapi.ApiException)2 HashMap (java.util.HashMap)2 Map (java.util.Map)2 Matcher (java.util.regex.Matcher)2 V1Container (io.kubernetes.client.openapi.models.V1Container)1 V1EnvVar (io.kubernetes.client.openapi.models.V1EnvVar)1 V1EnvVarSource (io.kubernetes.client.openapi.models.V1EnvVarSource)1 V1ObjectFieldSelector (io.kubernetes.client.openapi.models.V1ObjectFieldSelector)1 V1PodSpec (io.kubernetes.client.openapi.models.V1PodSpec)1 V1PodTemplate (io.kubernetes.client.openapi.models.V1PodTemplate)1 V1PodTemplateSpec (io.kubernetes.client.openapi.models.V1PodTemplateSpec)1