use of com.mesosphere.sdk.state.ConfigStoreException in project dcos-commons by mesosphere.
the class ConfigQueries method getConfiguration.
/**
* Produces the content of the provided configuration ID, or returns an error if that ID doesn't
* exist or the data couldn't be read.
*/
public static <T extends Configuration> Response getConfiguration(ConfigStore<T> configStore, String configurationId) {
LOGGER.info("Attempting to fetch config with id '{}'", configurationId);
UUID uuid;
try {
uuid = UUID.fromString(configurationId);
} catch (Exception ex) {
LOGGER.warn(String.format("Failed to parse requested configuration id '%s' as a UUID", configurationId), ex);
return Response.status(Response.Status.BAD_REQUEST).build();
}
try {
return fetchConfig(configStore, uuid);
} catch (ConfigStoreException ex) {
if (ex.getReason() == Reason.NOT_FOUND) {
LOGGER.warn(String.format("Requested configuration '%s' doesn't exist", configurationId), ex);
return Response.status(Response.Status.NOT_FOUND).build();
}
LOGGER.error(String.format("Failed to fetch requested configuration with id '%s'", configurationId), ex);
return Response.serverError().build();
}
}
use of com.mesosphere.sdk.state.ConfigStoreException in project dcos-commons by mesosphere.
the class TaskUtilsTest method buildPodLayout.
private static ConfigStore<ServiceSpec> buildPodLayout(int essentialTasks, int nonessentialTasks) {
DefaultPodSpec.Builder podBuilder = DefaultPodSpec.newBuilder("executor-uri").type("server").count(3);
for (int i = 0; i < essentialTasks; ++i) {
podBuilder.addTask(buildTaskTemplate(String.format("essential%d", i)).goalState(GoalState.RUNNING).build());
}
for (int i = 0; i < nonessentialTasks; ++i) {
podBuilder.addTask(buildTaskTemplate(String.format("nonessential%d", i)).goalState(GoalState.RUNNING).essential(false).build());
}
// should be ignored for recovery purposes:
podBuilder.addTask(buildTaskTemplate("once").goalState(GoalState.ONCE).build());
ServiceSpec serviceSpec = DefaultServiceSpec.newBuilder().name("svc").addPod(podBuilder.build()).build();
ConfigStore<ServiceSpec> configStore = new ConfigStore<>(DefaultServiceSpec.getConfigurationFactory(serviceSpec), new MemPersister());
try {
configStore.setTargetConfig(configStore.store(serviceSpec));
} catch (ConfigStoreException e) {
throw new IllegalStateException(e);
}
return configStore;
}
use of com.mesosphere.sdk.state.ConfigStoreException in project dcos-commons by mesosphere.
the class TaskUtilsTest method buildTask.
private static Protos.TaskInfo buildTask(ConfigStore<ServiceSpec> configStore, int index, String task) {
Protos.TaskInfo.Builder taskBuilder = Protos.TaskInfo.newBuilder().setTaskId(TestConstants.TASK_ID).setSlaveId(TestConstants.AGENT_ID).setName(String.format("server-%d-%s", index, task));
UUID id;
try {
id = configStore.getTargetConfig();
} catch (ConfigStoreException e) {
throw new IllegalStateException(e);
}
taskBuilder.setLabels(new TaskLabelWriter(taskBuilder).setIndex(index).setType("server").setTargetConfiguration(id).toProto());
return taskBuilder.build();
}
use of com.mesosphere.sdk.state.ConfigStoreException 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);
}
use of com.mesosphere.sdk.state.ConfigStoreException 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);
}
Aggregations