private static void validateNameAndApplicationAttributes(@RequestBody CanaryConfig canaryConfig) { if (StringUtils.isEmpty(canaryConfig.getName())) { throw new IllegalArgumentException("Canary config must specify a name."); } else if (canaryConfig.getApplications() == null || canaryConfig.getApplications().size() == 0) { throw new IllegalArgumentException("Canary config must specify at least one application."); } String canaryConfigName = canaryConfig.getName(); if (!canaryConfigNamePattern.matcher(canaryConfigName).matches()) { throw new IllegalArgumentException("Canary config cannot be named '" + canaryConfigName + "'. Names must contain only letters, numbers, dashes (-) and underscores (_)."); } }
public Map processQueryAndReturnMap(String metricsAccountName, String storageAccountName, CanaryConfig canaryConfig, CanaryMetricConfig canaryMetricConfig, int metricIndex, CanaryScope canaryScope, boolean dryRun) throws IOException { if (canaryConfig == null) { canaryConfig = CanaryConfig.builder().metric(canaryMetricConfig).build(); } if (dryRun) { MetricsService metricsService = metricsServiceRepository .getOne(metricsAccountName) .orElseThrow(() -> new IllegalArgumentException("No metrics service was configured; unable to read from metrics store.")); String query = metricsService.buildQuery(metricsAccountName, canaryConfig, canaryMetricConfig, canaryScope); return Collections.singletonMap("query", query); } else { String metricSetListId = executeQuery(metricsAccountName, storageAccountName, canaryConfig, metricIndex, canaryScope); return Collections.singletonMap("metricSetListId", metricSetListId); } }
String resolvedMetricsAccountName, String resolvedStorageAccountName) { return IntStream.range(0, canaryConfig.getMetrics().size()) .mapToObj(index -> { CanaryMetricConfig metric = canaryConfig.getMetrics().get(index); String serviceType = metric.getQuery().getServiceType(); CanaryScopeFactory canaryScopeFactory = getScopeFactoryForServiceType(serviceType);
@VisibleForTesting public static CanaryConfig escapeTemplates(CanaryConfig canaryConfig) { if (!CollectionUtils.isEmpty(canaryConfig.getTemplates())) { Map<String, String> escapedTemplates = canaryConfig.getTemplates() .entrySet() .stream() .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().replace("${", "$\\{"))); canaryConfig = canaryConfig.toBuilder().templates(escapedTemplates).build(); } return canaryConfig; }
@ApiOperation(value = "Write a canary config to object storage") @RequestMapping(consumes = "application/json", method = RequestMethod.POST) public CanaryConfigUpdateResponse storeCanaryConfig(@RequestParam(required = false) final String configurationAccountName, @RequestBody CanaryConfig canaryConfig) throws IOException { String resolvedConfigurationAccountName = CredentialsHelper.resolveAccountByNameOrType(configurationAccountName, AccountCredentials.Type.CONFIGURATION_STORE, accountCredentialsRepository); StorageService configurationService = storageServiceRepository .getOne(resolvedConfigurationAccountName) .orElseThrow(() -> new IllegalArgumentException("No configuration service was configured; unable to write canary config to bucket.")); if (canaryConfig.getCreatedTimestamp() == null) { canaryConfig.setCreatedTimestamp(System.currentTimeMillis()); } if (canaryConfig.getUpdatedTimestamp() == null) { canaryConfig.setUpdatedTimestamp(canaryConfig.getCreatedTimestamp()); } canaryConfig.setCreatedTimestampIso(Instant.ofEpochMilli(canaryConfig.getCreatedTimestamp()).toString()); canaryConfig.setUpdatedTimestampIso(Instant.ofEpochMilli(canaryConfig.getUpdatedTimestamp()).toString()); String canaryConfigId = UUID.randomUUID() + ""; // TODO(duftler): Serialize the canary config within a canary run? validateNameAndApplicationAttributes(canaryConfig); try { configurationService.loadObject(resolvedConfigurationAccountName, ObjectType.CANARY_CONFIG, canaryConfigId); } catch (NotFoundException e) { configurationService.storeObject(resolvedConfigurationAccountName, ObjectType.CANARY_CONFIG, canaryConfigId, canaryConfig, canaryConfig.getName() + ".json", false); return CanaryConfigUpdateResponse.builder().canaryConfigId(canaryConfigId).build(); } throw new IllegalArgumentException("Canary config '" + canaryConfigId + "' already exists."); }
@NotNull String resolvedStorageAccountName, @NotNull CanaryExecutionRequest canaryExecutionRequest) throws JsonProcessingException { registry.counter(pipelineRunId.withTag("canaryConfigId", canaryConfigId).withTag("canaryConfigName", canaryConfig.getName())).increment(); Set<String> requiredScopes = canaryConfig.getMetrics().stream() .map(CanaryMetricConfig::getScopeName) .filter(Objects::nonNull) resolvedStorageAccountName); int maxMetricIndex = canaryConfig.getMetrics().size() - 1; // 0 based naming, so we want the last index value, not the count String lastControlFetchRefid = CanaryStageNames.REFID_FETCH_CONTROL_PREFIX + maxMetricIndex; String lastExperimentFetchRefid = CanaryStageNames.REFID_FETCH_EXPERIMENT_PREFIX + maxMetricIndex;
@Override public List<Map<String, Object>> listObjectKeys(String accountName, ObjectType objectType, List<String> applications, boolean skipIndex) { MemoryNamedAccountCredentials credentials = getCredentials(accountName, objectType); boolean filterOnApplications = applications != null && applications.size() > 0; List<Map<String, Object>> result = new ArrayList<>(); for (Map.Entry<String, Object> entry : credentials.getObjects().get(objectType).entrySet()) { String entryKey = entry.getKey(); if (objectType == ObjectType.CANARY_CONFIG) { if (filterOnApplications) { CanaryConfig canaryConfig = (CanaryConfig)entry.getValue(); if (CanaryConfigIndex.haveCommonElements(applications, canaryConfig.getApplications())) { result.add(credentials.getMetadata().get(objectType).get(entryKey)); } } else { result.add(credentials.getMetadata().get(objectType).get(entryKey)); } } else { result.add(credentials.getMetadata().get(objectType).get(entryKey)); } } return result; } }
@ApiOperation(value = "Update a canary config") @RequestMapping(value = "/{canaryConfigId:.+}", consumes = "application/json", method = RequestMethod.PUT) public CanaryConfigUpdateResponse updateCanaryConfig(@RequestParam(required = false) final String configurationAccountName, @PathVariable String canaryConfigId, @RequestBody CanaryConfig canaryConfig) throws IOException { String resolvedConfigurationAccountName = CredentialsHelper.resolveAccountByNameOrType(configurationAccountName, AccountCredentials.Type.CONFIGURATION_STORE, accountCredentialsRepository); StorageService configurationService = storageServiceRepository .getOne(resolvedConfigurationAccountName) .orElseThrow(() -> new IllegalArgumentException("No configuration service was configured; unable to write canary config to bucket.")); canaryConfig.setUpdatedTimestamp(System.currentTimeMillis()); canaryConfig.setUpdatedTimestampIso(Instant.ofEpochMilli(canaryConfig.getUpdatedTimestamp()).toString()); validateNameAndApplicationAttributes(canaryConfig); try { configurationService.loadObject(resolvedConfigurationAccountName, ObjectType.CANARY_CONFIG, canaryConfigId); } catch (Exception e) { throw new IllegalArgumentException("Canary config '" + canaryConfigId + "' does not exist."); } configurationService.storeObject(resolvedConfigurationAccountName, ObjectType.CANARY_CONFIG, canaryConfigId, canaryConfig, canaryConfig.getName() + ".json", true); return CanaryConfigUpdateResponse.builder().canaryConfigId(canaryConfigId).build(); }
private Map<String, Object> metadataFor(ConfigBinNamedAccountCredentials credentials, String id) { // TODO: (mgraff) Should factor out to a common method, or just call .load() ConfigBinRemoteService remoteService = credentials.getRemoteService(); String ownerApp = credentials.getOwnerApp(); String configType = credentials.getConfigType(); String json; try { json = retry.retry(() -> remoteService.get(ownerApp, configType, id), MAX_RETRIES, RETRY_BACKOFF); } catch (RetrofitError e) { throw new IllegalArgumentException("No such object named " + id); } CanaryConfig config; try { config = kayentaObjectMapper.readValue(json, ObjectType.CANARY_CONFIG.getTypeReference()); } catch (Throwable e) { log.error("Read failed on path {}: {}", id, e); throw new IllegalStateException(e); } return new ImmutableMap.Builder<String, Object>() .put("id", id) .put("name", config.getName()) .build(); } }
CanaryJudgeConfig canaryJudgeConfig = canaryConfig.getJudge(); CanaryJudge canaryJudge = null;
templateToExpand = unescapeTemplate(customInlineTemplate); } else { Map<String, String> templates = canaryConfig.getTemplates();
@NotNull String resolvedStorageAccountName, @NotNull CanaryExecutionRequest canaryExecutionRequest) throws JsonProcessingException { registry.counter(pipelineRunId.withTag("canaryConfigId", canaryConfigId).withTag("canaryConfigName", canaryConfig.getName())).increment(); Set<String> requiredScopes = canaryConfig.getMetrics().stream() .map(CanaryMetricConfig::getScopeName) .filter(Objects::nonNull) resolvedStorageAccountName); int maxMetricIndex = canaryConfig.getMetrics().size() - 1; // 0 based naming, so we want the last index value, not the count String lastControlFetchRefid = CanaryStageNames.REFID_FETCH_CONTROL_PREFIX + maxMetricIndex; String lastExperimentFetchRefid = CanaryStageNames.REFID_FETCH_EXPERIMENT_PREFIX + maxMetricIndex;
List<String> applications = canaryConfig.getApplications();
@VisibleForTesting public static CanaryConfig escapeTemplates(CanaryConfig canaryConfig) { if (!CollectionUtils.isEmpty(canaryConfig.getTemplates())) { Map<String, String> escapedTemplates = canaryConfig.getTemplates() .entrySet() .stream() .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().replace("${", "$\\{"))); canaryConfig = canaryConfig.toBuilder().templates(escapedTemplates).build(); } return canaryConfig; }
CanaryJudgeConfig canaryJudgeConfig = canaryConfig.getJudge(); CanaryJudge canaryJudge = null;
templateToExpand = unescapeTemplate(customInlineTemplate); } else { Map<String, String> templates = canaryConfig.getTemplates();
private void checkForDuplicateCanaryConfig(CanaryConfig canaryConfig, String canaryConfigId, AwsNamedAccountCredentials credentials) { String canaryConfigName = canaryConfig.getName(); List<String> applications = canaryConfig.getApplications(); String existingCanaryConfigId = canaryConfigIndex.getIdFromName(credentials, canaryConfigName, applications); // We want to avoid creating a naming collision due to the renaming of an existing canary config. if (!StringUtils.isEmpty(existingCanaryConfigId) && !existingCanaryConfigId.equals(canaryConfigId)) { throw new IllegalArgumentException("Canary config with name '" + canaryConfigName + "' already exists in the scope of applications " + applications + "."); } }
@Override public List<Map<String, Object>> listObjectKeys(String accountName, ObjectType objectType, List<String> applications, boolean skipIndex) { MemoryNamedAccountCredentials credentials = getCredentials(accountName, objectType); boolean filterOnApplications = applications != null && applications.size() > 0; List<Map<String, Object>> result = new ArrayList<>(); for (Map.Entry<String, Object> entry : credentials.getObjects().get(objectType).entrySet()) { String entryKey = entry.getKey(); if (objectType == ObjectType.CANARY_CONFIG) { if (filterOnApplications) { CanaryConfig canaryConfig = (CanaryConfig)entry.getValue(); if (CanaryConfigIndex.haveCommonElements(applications, canaryConfig.getApplications())) { result.add(credentials.getMetadata().get(objectType).get(entryKey)); } } else { result.add(credentials.getMetadata().get(objectType).get(entryKey)); } } else { result.add(credentials.getMetadata().get(objectType).get(entryKey)); } } return result; } }
CanaryConfig.builder().metric(canaryMetricConfig).build(), 0, canaryScope);
CanaryMetricConfig canaryMetricConfig = canaryConfig.getMetrics().get(metricIndex); List<MetricSet> metricSetList = null; int retries = 0;