Search in sources :

Example 31 with ServiceSpec

use of com.mesosphere.sdk.specification.ServiceSpec in project dcos-commons by mesosphere.

the class DefaultConfigurationUpdater method cleanupDuplicateAndUnusedConfigs.

/**
 * Searches for any task configurations which are already identical to the target configuration
 * and updates the embedded config version label in those tasks to point to the current target
 * configuration.
 */
private void cleanupDuplicateAndUnusedConfigs(ServiceSpec targetConfig, UUID targetConfigId) throws ConfigStoreException {
    List<Protos.TaskInfo> taskInfosToUpdate = new ArrayList<>();
    Set<UUID> neededConfigs = new HashSet<>();
    neededConfigs.add(targetConfigId);
    // Search task labels for configs which need to be cleaned up.
    for (Protos.TaskInfo taskInfo : stateStore.fetchTasks()) {
        final UUID taskConfigId;
        try {
            taskConfigId = new TaskLabelReader(taskInfo).getTargetConfiguration();
        } catch (TaskException e) {
            LOGGER.warn(String.format("Unable to extract configuration ID from task %s: %s", taskInfo.getName(), TextFormat.shortDebugString(taskInfo)), e);
            continue;
        }
        if (taskConfigId.equals(targetConfigId)) {
            LOGGER.info("Task {} configuration ID matches target: {}", taskInfo.getName(), taskConfigId);
        } else {
            try {
                final ServiceSpec taskConfig = configStore.fetch(taskConfigId);
                if (!needsConfigUpdate(taskInfo, targetConfig, taskConfig)) {
                    // Task is effectively already on the target config. Update task's config ID to match target,
                    // and allow the duplicate config to be dropped from configStore.
                    TaskInfo.Builder taskBuilder = taskInfo.toBuilder();
                    taskBuilder.setLabels(new TaskLabelWriter(taskInfo).setTargetConfiguration(targetConfigId).toProto());
                    taskInfosToUpdate.add(taskBuilder.build());
                } else {
                    // Config isn't the same as the target. Refrain from updating task, mark config as 'needed'.
                    neededConfigs.add(taskConfigId);
                }
            } catch (Exception e) {
                LOGGER.error(String.format("Failed to fetch configuration %s for task %s", taskConfigId, taskInfo.getName()), e);
                // Cannot read this task's config. Do not delete the config.
                neededConfigs.add(taskConfigId);
            }
        }
    }
    if (!taskInfosToUpdate.isEmpty()) {
        LOGGER.info("Updating {} tasks in StateStore with target configuration ID {}", taskInfosToUpdate.size(), targetConfigId);
        stateStore.storeTasks(taskInfosToUpdate);
    }
    Collection<UUID> configIds = configStore.list();
    LOGGER.info("Testing deserialization of {} listed configurations before cleanup:", configIds.size());
    for (UUID configId : configIds) {
        try {
            configStore.fetch(configId);
            LOGGER.info("- {}: OK", configId);
        } catch (Exception e) {
            LOGGER.info("- {}: FAILED, leaving as-is: {}", configId, e.getMessage());
            neededConfigs.add(configId);
        }
    }
    clearConfigsNotListed(neededConfigs);
}
Also used : TaskLabelReader(com.mesosphere.sdk.offer.taskdata.TaskLabelReader) DefaultServiceSpec(com.mesosphere.sdk.specification.DefaultServiceSpec) ServiceSpec(com.mesosphere.sdk.specification.ServiceSpec) TaskInfo(org.apache.mesos.Protos.TaskInfo) TaskException(com.mesosphere.sdk.offer.TaskException) ConfigStoreException(com.mesosphere.sdk.state.ConfigStoreException) TaskInfo(org.apache.mesos.Protos.TaskInfo) TaskException(com.mesosphere.sdk.offer.TaskException) Protos(org.apache.mesos.Protos) TaskLabelWriter(com.mesosphere.sdk.offer.taskdata.TaskLabelWriter)

Example 32 with ServiceSpec

use of com.mesosphere.sdk.specification.ServiceSpec in project dcos-commons by mesosphere.

the class DefaultConfigurationUpdater method updateConfiguration.

@Override
public UpdateResult updateConfiguration(ServiceSpec candidateConfig) throws ConfigStoreException {
    // Get the currently stored target configuration
    UUID targetConfigId;
    try {
        targetConfigId = configStore.getTargetConfig();
    } catch (ConfigStoreException e) {
        LOGGER.debug("No target configuration ID was set. First launch?");
        targetConfigId = null;
    }
    Optional<ServiceSpec> targetConfig;
    if (targetConfigId != null) {
        LOGGER.info("Loading current target configuration: {}", targetConfigId);
        targetConfig = Optional.of(configStore.fetch(targetConfigId));
    } else {
        targetConfig = Optional.empty();
    }
    // Log the config state (with diff of changes vs prior state) before proceeding with checks.
    final List<ConfigValidationError> errors = new ArrayList<>();
    String candidateConfigJson = null;
    try {
        candidateConfigJson = candidateConfig.toJsonString();
        LOGGER.info("New prospective config:\n{}", candidateConfigJson);
    } catch (Exception e) {
        LOGGER.error(String.format("Unable to get JSON representation of new prospective config object: %s", candidateConfig), e);
        errors.add(ConfigValidationError.valueError("NewConfigAsJson", "jsonString", String.format("Unable to serialize new config to JSON for logging: %s", e.getMessage())));
    }
    if (!targetConfig.isPresent()) {
        LOGGER.info("Skipping config diff: There is no old config target to diff against");
    } else if (candidateConfigJson == null) {
        LOGGER.error("Skipping config diff: New target couldn't be represented as JSON");
    } else {
        LOGGER.info("Prior target config:\n{}", targetConfig.get().toJsonString());
        printConfigDiff(targetConfig.get(), targetConfigId, candidateConfigJson);
    }
    targetConfig = fixServiceSpecUser(targetConfig);
    // whether it's considered equal by the ConfigComparator.
    for (ConfigValidator<ServiceSpec> validator : validators) {
        errors.addAll(validator.validate(targetConfig, candidateConfig));
    }
    // there are validation errors against the new config, we continue using the prior target.
    if (!errors.isEmpty()) {
        StringJoiner sj = new StringJoiner("\n");
        int i = 1;
        for (ConfigValidationError error : errors) {
            sj.add(String.format("%d: %s", i++, error.toString()));
        }
        LOGGER.warn("New configuration failed validation against current target " + "configuration {}, with {} errors across {} validators:\n{}", targetConfigId, errors.size(), validators.size(), sj.toString());
        if (!targetConfig.isPresent()) {
            throw new ConfigStoreException(Reason.LOGIC_ERROR, String.format("Configuration failed validation without any prior target configuration" + "available for fallback. Initial launch with invalid configuration? " + "%d Errors: %s", errors.size(), sj.toString()));
        }
    } else if (!targetConfig.isPresent() || !configComparator.equals(targetConfig.get(), candidateConfig)) {
        UUID oldTargetId = targetConfigId;
        targetConfigId = configStore.store(candidateConfig);
        LOGGER.info("Updating target configuration: " + "Prior target configuration '{}' is different from new configuration '{}'. ", oldTargetId, targetConfigId);
        targetConfig = Optional.of(candidateConfig);
        configStore.setTargetConfig(targetConfigId);
    } else {
        LOGGER.info("No changes detected between current target configuration '{}' and new configuration. " + "Leaving current configuration as the target.", targetConfigId);
    }
    // Update config IDs on tasks whose config contents match the current target, then clean up
    // leftover configs which are not the target and which are not referenced by any tasks.
    cleanupDuplicateAndUnusedConfigs(targetConfig.get(), targetConfigId);
    return new ConfigurationUpdater.UpdateResult(targetConfigId, errors);
}
Also used : ConfigStoreException(com.mesosphere.sdk.state.ConfigStoreException) DefaultServiceSpec(com.mesosphere.sdk.specification.DefaultServiceSpec) ServiceSpec(com.mesosphere.sdk.specification.ServiceSpec) TaskException(com.mesosphere.sdk.offer.TaskException) ConfigStoreException(com.mesosphere.sdk.state.ConfigStoreException) ConfigValidationError(com.mesosphere.sdk.config.validate.ConfigValidationError)

Example 33 with ServiceSpec

use of com.mesosphere.sdk.specification.ServiceSpec in project dcos-commons by mesosphere.

the class DomainCapabilityValidator method validate.

@Override
public Collection<ConfigValidationError> validate(Optional<ServiceSpec> oldConfig, ServiceSpec newConfig) {
    if (Capabilities.getInstance().supportsDomains()) {
        return Collections.emptyList();
    }
    List<PodSpec> podSpecs = newConfig.getPods().stream().filter(podSpec -> podSpec.getPlacementRule().isPresent()).collect(Collectors.toList());
    List<ConfigValidationError> errors = new ArrayList<>();
    for (PodSpec podSpec : podSpecs) {
        PlacementRule placementRule = podSpec.getPlacementRule().get();
        if (PlacementUtils.placementRuleReferencesZone(podSpec)) {
            String errMsg = String.format("The PlacementRule for PodSpec '%s' may not reference Zones prior to DC/OS 1.11.", podSpec.getType());
            errors.add(ConfigValidationError.valueError("PlacementRule", placementRule.toString(), errMsg));
        }
        if (PlacementUtils.placementRuleReferencesRegion(podSpec)) {
            String errMsg = String.format("The PlacementRule for PodSpec '%s' may not reference Regions prior to DC/OS 1.11.", podSpec.getType());
            errors.add(ConfigValidationError.valueError("PlacementRule", placementRule.toString(), errMsg));
        }
    }
    return errors;
}
Also used : PodSpec(com.mesosphere.sdk.specification.PodSpec) Capabilities(com.mesosphere.sdk.dcos.Capabilities) java.util(java.util) PlacementRule(com.mesosphere.sdk.offer.evaluate.placement.PlacementRule) PlacementUtils(com.mesosphere.sdk.offer.evaluate.placement.PlacementUtils) Collectors(java.util.stream.Collectors) ServiceSpec(com.mesosphere.sdk.specification.ServiceSpec) PodSpec(com.mesosphere.sdk.specification.PodSpec) PlacementRule(com.mesosphere.sdk.offer.evaluate.placement.PlacementRule)

Example 34 with ServiceSpec

use of com.mesosphere.sdk.specification.ServiceSpec in project dcos-commons by mesosphere.

the class ConfigurationUpdaterTest method testUserSetAtServiceLevelButNotPodLevel.

@Test
public void testUserSetAtServiceLevelButNotPodLevel() throws ConfigStoreException {
    final ConfigurationUpdater<ServiceSpec> configurationUpdater = new DefaultConfigurationUpdater(mockStateStore, mockConfigStore, DefaultServiceSpec.getComparatorInstance(), DefaultConfigValidators.getValidators(SchedulerConfigTestUtils.getTestSchedulerConfig()));
    when(mockConfigStore.getTargetConfig()).thenReturn(TARGET_ID);
    DefaultServiceSpec.Builder serviceSpecWithUser = DefaultServiceSpec.newBuilder(ORIGINAL_SERVICE_SPECIFICATION).user(DcosConstants.DEFAULT_SERVICE_USER);
    List<PodSpec> podsWithoutUsers = new ArrayList<>();
    for (PodSpec podSpec : ORIGINAL_SERVICE_SPECIFICATION.getPods()) {
        podsWithoutUsers.add(DefaultPodSpec.newBuilder(podSpec).user(null).build());
    }
    serviceSpecWithUser.pods(podsWithoutUsers);
    when(mockConfigStore.fetch(TARGET_ID)).thenReturn(serviceSpecWithUser.build());
    // Note: the new service spec sets pod users with a non-root user
    ConfigurationUpdater.UpdateResult result = configurationUpdater.updateConfiguration(ORIGINAL_SERVICE_SPECIFICATION_WITH_USER);
    Assert.assertEquals(TARGET_ID, result.getTargetId());
    // since the 2 pods don't set the user their user defaults to "root" which conflicts with the user set as noted above
    Assert.assertEquals(2, result.getErrors().size());
}
Also used : DefaultPodSpec(com.mesosphere.sdk.specification.DefaultPodSpec) PodSpec(com.mesosphere.sdk.specification.PodSpec) DefaultServiceSpec(com.mesosphere.sdk.specification.DefaultServiceSpec) ServiceSpec(com.mesosphere.sdk.specification.ServiceSpec) DefaultServiceSpec(com.mesosphere.sdk.specification.DefaultServiceSpec) Test(org.junit.Test)

Example 35 with ServiceSpec

use of com.mesosphere.sdk.specification.ServiceSpec in project dcos-commons by mesosphere.

the class PodSpecsCannotShrinkTest method testMatchingSizeIsValid.

@Test
public void testMatchingSizeIsValid() throws InvalidRequirementException {
    ServiceSpec serviceSpec1 = getServiceSpec(mockPodSpecA1, mockPodSpecB1);
    ServiceSpec serviceSpec2 = getServiceSpec("svc2", mockPodSpecA1, mockPodSpecB1);
    Assert.assertEquals(0, VALIDATOR.validate(Optional.of(serviceSpec1), serviceSpec2).size());
    Assert.assertEquals(0, VALIDATOR.validate(Optional.of(serviceSpec2), serviceSpec1).size());
    Assert.assertEquals(0, VALIDATOR.validate(Optional.of(serviceSpec1), serviceSpec1).size());
    Assert.assertEquals(0, VALIDATOR.validate(Optional.of(serviceSpec2), serviceSpec2).size());
}
Also used : DefaultServiceSpec(com.mesosphere.sdk.specification.DefaultServiceSpec) ServiceSpec(com.mesosphere.sdk.specification.ServiceSpec) Test(org.junit.Test)

Aggregations

ServiceSpec (com.mesosphere.sdk.specification.ServiceSpec)61 DefaultServiceSpec (com.mesosphere.sdk.specification.DefaultServiceSpec)55 Test (org.junit.Test)51 MemPersister (com.mesosphere.sdk.storage.MemPersister)7 PodSpec (com.mesosphere.sdk.specification.PodSpec)6 Collectors (java.util.stream.Collectors)5 ConfigStoreException (com.mesosphere.sdk.state.ConfigStoreException)3 java.util (java.util)3 Protos (org.apache.mesos.Protos)3 Capabilities (com.mesosphere.sdk.dcos.Capabilities)2 TaskException (com.mesosphere.sdk.offer.TaskException)2 PlacementRule (com.mesosphere.sdk.offer.evaluate.placement.PlacementRule)2 TaskLabelWriter (com.mesosphere.sdk.offer.taskdata.TaskLabelWriter)2 DefaultPodSpec (com.mesosphere.sdk.specification.DefaultPodSpec)2 Persister (com.mesosphere.sdk.storage.Persister)2 PersisterException (com.mesosphere.sdk.storage.PersisterException)2 ArrayList (java.util.ArrayList)2 Collection (java.util.Collection)2 List (java.util.List)2 Optional (java.util.Optional)2