Skip to content

Commit

Permalink
Add metric set list controller capable of round-tripping metric set l…
Browse files Browse the repository at this point in the history
…ists to/from object store. (#12)

Modify /fetch/query to return uuid of newly-created object.
Treat canaryConfigUUID as path variable instead of request param on lookup.
  • Loading branch information
Matt Duftler committed May 16, 2017
1 parent a23af0b commit d2e8f5a
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
package com.netflix.kayenta.metrics;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Singular;
import lombok.ToString;
import org.springframework.util.StringUtils;
Expand All @@ -31,6 +33,8 @@

@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class MetricSet {

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@

package com.netflix.kayenta.storage;

import com.fasterxml.jackson.core.type.TypeReference;
import com.netflix.kayenta.canary.CanaryConfig;
import com.netflix.kayenta.metrics.MetricSet;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;

@AllArgsConstructor
public enum ObjectType {
CANARY_CONFIG(CanaryConfig.class, "canary_config", "canary_config.json"),
METRIC_SET(MetricSet.class, "metrics", "metric_set.json");
CANARY_CONFIG(new TypeReference<CanaryConfig>() {}, "canary_config", "canary_config.json"),
METRIC_SET_LIST(new TypeReference<List<MetricSet>>() {}, "metrics", "metric_set.json");

@Getter
final Class<?> clazz;
final TypeReference typeReference;

@Getter
final String group;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

package com.netflix.kayenta.gcs.storage;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.HttpResponseException;
import com.google.api.services.storage.Storage;
import com.google.api.services.storage.model.Bucket;
import com.google.api.services.storage.model.StorageObject;
import com.netflix.kayenta.google.security.GoogleNamedAccountCredentials;
import com.netflix.kayenta.metrics.MetricSet;
import com.netflix.kayenta.security.AccountCredentialsRepository;
import com.netflix.kayenta.storage.ObjectType;
import com.netflix.kayenta.storage.StorageService;
Expand Down Expand Up @@ -69,7 +71,7 @@ public <T> T loadObject(String accountName, ObjectType objectType, String object
try {
StorageObject storageObject = storage.objects().get(bucketName, path).execute();

return deserialize(storage, storageObject, (Class<T>)objectType.getClazz());
return deserialize(storage, storageObject, objectType.getTypeReference());
} catch (IOException e) {
if (e instanceof HttpResponseException) {
HttpResponseException hre = (HttpResponseException)e;
Expand All @@ -82,13 +84,13 @@ public <T> T loadObject(String accountName, ObjectType objectType, String object
}
}

private <T> T deserialize(Storage storage, StorageObject object, Class<T> clazz) throws IOException {
private <T> T deserialize(Storage storage, StorageObject object, TypeReference typeReference) throws IOException {
ByteArrayOutputStream output = new java.io.ByteArrayOutputStream();
Storage.Objects.Get getter = storage.objects().get(object.getBucket(), object.getName());
getter.executeMediaAndDownloadTo(output);
String json = output.toString("UTF8");

return objectMapper.readValue(json, clazz);
return objectMapper.readValue(json, typeReference);
}

@Override
Expand Down
1 change: 1 addition & 0 deletions kayenta-web/config/kayenta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ swagger:
- /canaryConfig.*
- /credentials.*
- /fetch.*
- /metricSetList.*
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.netflix.kayenta.storage.StorageServiceRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
Expand All @@ -45,16 +46,18 @@ public class CanaryConfigController {
@Autowired
StorageServiceRepository storageServiceRepository;

@RequestMapping(method = RequestMethod.GET)
// TODO(duftler): Lookup by name.

@RequestMapping(value = "/{canaryConfigId:.+}", method = RequestMethod.GET)
public CanaryConfig loadCanaryConfig(@RequestParam(required = false) final String accountName,
@RequestParam String canaryConfigUUID) {
@PathVariable String canaryConfigId) {
String resolvedAccountName = CredentialsHelper.resolveAccountByNameOrType(accountName,
AccountCredentials.Type.OBJECT_STORE,
accountCredentialsRepository);
Optional<StorageService> storageService = storageServiceRepository.getOne(resolvedAccountName);

if (storageService.isPresent()) {
return storageService.get().loadObject(resolvedAccountName, ObjectType.CANARY_CONFIG, canaryConfigUUID);
return storageService.get().loadObject(resolvedAccountName, ObjectType.CANARY_CONFIG, canaryConfigId);
} else {
log.debug("No storage service was configured; skipping placeholder logic to read from bucket.");
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ public class FetchController {
@Autowired
StorageServiceRepository storageServiceRepository;

@RequestMapping(method = RequestMethod.GET)
public List<MetricSet> queryMetrics(@RequestParam(required = false) final String accountName,
@ApiParam(defaultValue = "cpu") @RequestParam String metricSetName,
@ApiParam(defaultValue = "myapp-v010-") @RequestParam String instanceNamePrefix,
@ApiParam(defaultValue = "2017-03-24T15:13:00Z") @RequestParam String intervalStartTime,
@ApiParam(defaultValue = "2017-03-24T15:27:00Z") @RequestParam String intervalEndTime) throws IOException {
@RequestMapping(value = "/query", method = RequestMethod.GET)
public String queryMetrics(@RequestParam(required = false) final String accountName,
@ApiParam(defaultValue = "cpu") @RequestParam String metricSetName,
@ApiParam(defaultValue = "myapp-v010-") @RequestParam String instanceNamePrefix,
@ApiParam(defaultValue = "2017-05-01T15:13:00Z") @RequestParam String intervalStartTime,
@ApiParam(defaultValue = "2017-05-02T15:27:00Z") @RequestParam String intervalEndTime) throws IOException {
String resolvedAccountName = CredentialsHelper.resolveAccountByNameOrType(accountName,
AccountCredentials.Type.METRICS_STORE,
accountCredentialsRepository);
Expand All @@ -77,13 +77,14 @@ public List<MetricSet> queryMetrics(@RequestParam(required = false) final String
// TODO(duftler): This is placeholder logic. Just demonstrating that we can write to the bucket.
// It is not expected that this would (necessarily) use the same account name as that used for the metrics store.
Optional<StorageService> storageService = storageServiceRepository.getOne(resolvedAccountName);
String metricSetListId = UUID.randomUUID() + "";

if (storageService.isPresent()) {
storageService.get().storeObject(resolvedAccountName, ObjectType.METRIC_SET, UUID.randomUUID() + "", metricSetList);
storageService.get().storeObject(resolvedAccountName, ObjectType.METRIC_SET_LIST, metricSetListId, metricSetList);
} else {
log.debug("No storage service was configured; skipping placeholder logic to write to bucket.");
}

return metricSetList;
return metricSetListId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2017 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.kayenta.controllers;

import com.netflix.kayenta.canary.CanaryConfig;
import com.netflix.kayenta.metrics.MetricSet;
import com.netflix.kayenta.security.AccountCredentials;
import com.netflix.kayenta.security.AccountCredentialsRepository;
import com.netflix.kayenta.storage.ObjectType;
import com.netflix.kayenta.storage.StorageService;
import com.netflix.kayenta.storage.StorageServiceRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@RestController
@RequestMapping("/metricSetList")
@Slf4j
public class MetricSetListController {

@Autowired
AccountCredentialsRepository accountCredentialsRepository;

@Autowired
StorageServiceRepository storageServiceRepository;

@RequestMapping(value = "/{metricSetListId:.+}", method = RequestMethod.GET)
public List<MetricSet> loadMetricSetList(@RequestParam(required = false) final String accountName,
@PathVariable String metricSetListId) {
String resolvedAccountName = CredentialsHelper.resolveAccountByNameOrType(accountName,
AccountCredentials.Type.OBJECT_STORE,
accountCredentialsRepository);
Optional<StorageService> storageService = storageServiceRepository.getOne(resolvedAccountName);

if (storageService.isPresent()) {
return storageService.get().loadObject(resolvedAccountName, ObjectType.METRIC_SET_LIST, metricSetListId);
} else {
log.debug("No storage service was configured; skipping placeholder logic to read from bucket.");
return null;
}
}

@RequestMapping(consumes = "application/context+json", method = RequestMethod.POST)
public String storeMetricSetList(@RequestParam(required = false) final String accountName,
@RequestBody List<MetricSet> metricSetList) throws IOException {
String resolvedAccountName = CredentialsHelper.resolveAccountByNameOrType(accountName,
AccountCredentials.Type.OBJECT_STORE,
accountCredentialsRepository);
Optional<StorageService> storageService = storageServiceRepository.getOne(resolvedAccountName);
String metricSetListId = UUID.randomUUID() + "";

if (storageService.isPresent()) {
storageService.get().storeObject(resolvedAccountName, ObjectType.METRIC_SET_LIST, metricSetListId, metricSetList);
} else {
log.debug("No storage service was configured; skipping placeholder logic to write to bucket.");
}

return metricSetListId;
}
}

0 comments on commit d2e8f5a

Please sign in to comment.