Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closes #824 - Added API for kapacitor tasks #862

Merged
merged 2 commits into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions components/inspectit-ocelot-configurationserver/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ dependencies {
"org.springframework.security:spring-security-ldap",
'org.springframework.boot:spring-boot-starter-actuator',

'org.apache.httpcomponents:httpclient:4.5.12', //Required for PATCH-Requests

"org.xerial:sqlite-jdbc:3.28.0",
"com.github.gwenn:sqlite-dialect:0.1.0",

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package rocks.inspectit.ocelot.rest.alert.kapacitor;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;
import rocks.inspectit.ocelot.config.model.InspectitServerSettings;
import rocks.inspectit.ocelot.rest.alert.kapacitor.model.Task;
import rocks.inspectit.ocelot.security.config.UserRoleConfiguration;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

@RestController
public class KapacitorTaskController extends KapacitorBaseController {

@Autowired
public KapacitorTaskController(InspectitServerSettings settings) {
super(settings);
}

@ApiOperation(value = "Provides a list with basic information about each kapacitor task. Only tasks based on templates will be listed.")
@GetMapping("/alert/kapacitor/tasks")
public List<Task> getAllTasks() {
ObjectNode response = kapacitor()
.getForEntity("/kapacitor/v1/tasks", ObjectNode.class)
.getBody();

return StreamSupport.stream(response.path("tasks").spliterator(), false)
.map(Task::fromKapacitorResponse)
.filter(task -> task.getTemplate() != null)
.collect(Collectors.toList());
}

@ApiOperation(value = "Provides detailed information about a given kapacitor task")

@GetMapping("/alert/kapacitor/tasks/{taskId}")
public Task getTask(@PathVariable @ApiParam("The id of the task to query") String taskId) {
ObjectNode response = kapacitor()
.getForEntity("/kapacitor/v1/tasks/{taskId}", ObjectNode.class, taskId)
.getBody();

return Task.fromKapacitorResponse(response);
}

@Secured(UserRoleConfiguration.WRITE_ACCESS_ROLE)
@ApiOperation(value = "Inserts a new Kapacitor task")
@PostMapping("/alert/kapacitor/tasks")
public Task addTask(@RequestBody Task task) {
ObjectNode response = kapacitor()
.postForEntity("/kapacitor/v1/tasks", task.toKapacitorRequest(), ObjectNode.class)
.getBody();

return Task.fromKapacitorResponse(response);
}

@Secured(UserRoleConfiguration.WRITE_ACCESS_ROLE)
@ApiOperation(value = "Updates one or more settings of a kapacitor task")
@PatchMapping("/alert/kapacitor/tasks/{taskId}")
public Task updateTask(@PathVariable @ApiParam("The id of the task to update") String taskId,
@RequestBody Task task) {
ObjectNode response = kapacitor()
.patchForObject("/kapacitor/v1/tasks/{taskId}", task.toKapacitorRequest(), ObjectNode.class, taskId);

return Task.fromKapacitorResponse(response);
}

@Secured(UserRoleConfiguration.WRITE_ACCESS_ROLE)
@ApiOperation(value = "Removes a task")
@DeleteMapping("/alert/kapacitor/tasks/{taskId}")
public void removeTask(@PathVariable @ApiParam("The id of the task to delete") String taskId) {
kapacitorRestTemplate.delete("/kapacitor/v1/tasks/{taskId}", taskId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ public List<Template> getAllTemplates() {
}

@ApiOperation(value = "Provides detailed information about a given kapacitor template")
@ApiParam(name = "templateId", value = "The id of the template to query")
@GetMapping("/alert/kapacitor/templates/{templateId}")
public Template getTemplate(@PathVariable String templateId) {
public Template getTemplate(@PathVariable @ApiParam("The id of the template to query") String templateId) {
ObjectNode response = kapacitor()
.getForEntity("/kapacitor/v1/templates/{templateId}", ObjectNode.class, templateId)
.getBody();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package rocks.inspectit.ocelot.rest.alert.kapacitor.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* Represents a single Kapacitor task.
* Only supports task properties of template-based tasks.
*/
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
@AllArgsConstructor
@NoArgsConstructor
public class Task {

String id;

/**
* Does not actually exist in kapacitor as first-level entity.
* Instead, this value is assumed to be stored in the Variable {@link TemplateVariable#TOPIC_VARIABLE};
*/
String topic;

/**
* Does not actually exist in kapacitor as first-level entity.
* Instead, this value is assumed to be stored in the Variable {@link TemplateVariable#TEMPLATE_REFERENCE_VARIABLE};
*/
String template;

String status;

/**
* Does not actually exist in Kapacitor as first-level entity.
* Instead, this value is assumed to be stored in the Variable {@link TemplateVariable#TEMPLATE_DESCRIPTION_VARIABLE};
*/
String description;

String error;

String created;

String modified;

@JsonProperty("last-enabled")
String lastEnabled;

Boolean executing;

List<TemplateVariable> vars;

/**
* @return a JSON-Object which can be used for adding or updating this task in POST/PATCH request to kapacitor
*/
public ObjectNode toKapacitorRequest() {
ObjectMapper mapper = new ObjectMapper();
ObjectNode result = mapper.createObjectNode();
ObjectNode varsNode = mapper.createObjectNode();
if (id != null) {
result.put("id", id);
}
if (status != null) {
result.put("status", status);
}
if (template != null) {
result.put("template-id", template);
TemplateVariable.builder()
.name(TemplateVariable.TEMPLATE_REFERENCE_VARIABLE)
.type("string")
.value(template)
.build()
.insertIntoKapacitorVars(mapper, varsNode);
}
if (topic != null) {
TemplateVariable.builder()
.name(TemplateVariable.TOPIC_VARIABLE)
.type("string")
.value(topic)
.build()
.insertIntoKapacitorVars(mapper, varsNode);
}
if (description != null) {
TemplateVariable.builder()
.name(TemplateVariable.TEMPLATE_DESCRIPTION_VARIABLE)
.type("string")
.value(description)
.build()
.insertIntoKapacitorVars(mapper, varsNode);
}
if (vars != null) {
vars.forEach(var -> var.insertIntoKapacitorVars(mapper, varsNode));
}
if (varsNode.size() > 0) {
result.set("vars", varsNode);
}
return result;

}

public static Task fromKapacitorResponse(JsonNode task) {
Task.TaskBuilder builder = Task.builder()
.id(task.path("id").asText())
.status(task.path("status").asText(null))
.error(task.path("error").asText(null))
.created(task.path("created").asText(null))
.modified(task.path("modified").asText(null))
.lastEnabled(task.path("last-enabled").asText(null))
.executing(getOptionalBoolean(task, "executing"));
if (task.has("vars")) {
decodeVariables(task.path("vars"), builder);
}
if (task.has("template-id")) {
//override the template setting from the dummy variable with teh actual value if available
builder.template(task.get("template-id").asText());
}
return builder.build();
}

private static Boolean getOptionalBoolean(JsonNode node, String path) {
JsonNode subNode = node.path(path);
return subNode.isNull() ? null : subNode.booleanValue();
}

private static void decodeVariables(JsonNode variables, Task.TaskBuilder builder) {
List<TemplateVariable> resultVars = new ArrayList<>();
Iterator<String> nameIterator = variables.fieldNames();
while (nameIterator.hasNext()) {
String name = nameIterator.next();
JsonNode var = variables.path(name);
if (TemplateVariable.TEMPLATE_DESCRIPTION_VARIABLE.equals(name)) {
builder.description(var.path("value").asText());
} else if (TemplateVariable.TOPIC_VARIABLE.equals(name)) {
builder.topic(var.path("value").asText());
} else if (TemplateVariable.TEMPLATE_REFERENCE_VARIABLE.equals(name)) {
builder.template(var.path("value").asText());
} else {
resultVars.add(TemplateVariable.fromKapacitorResponse(name, var));
}
}
builder.vars(resultVars);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.convert.DurationStyle;
import rocks.inspectit.ocelot.rest.util.DurationUtil;

import java.time.Duration;

/**
* Structure representing a Kapacitor Variable or Variable Definition.
*/
@Value
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
@AllArgsConstructor
@NoArgsConstructor
public class TemplateVariable {

/**
Expand All @@ -23,6 +30,12 @@ public class TemplateVariable {
*/
public static final String TEMPLATE_DESCRIPTION_VARIABLE = "inspectit_template_description";

/**
* This is a special field used by the inspectIT UI.
* It is used to store the reference to the template from which this task is constructed.
*/
public static final String TEMPLATE_REFERENCE_VARIABLE = "inspectit_template_reference";

/**
* This is a special field used by the inspectIT UI.
* It references a topic, to which the alerts will be sent.
Expand All @@ -43,6 +56,29 @@ public class TemplateVariable {
@JsonInclude(JsonInclude.Include.ALWAYS)
Object value;

/**
* Inserts thius variable into the provided "vars" Object node.
* This method can be used to add this variable to the "vars" of a given task.
*
* @param mapper the ObjectMapper for creating new nodes
* @param varsNode the "vars" map in which the variable definition will be placed.
*/
public void insertIntoKapacitorVars(ObjectMapper mapper, ObjectNode varsNode) {
ObjectNode subNode = mapper.createObjectNode();
subNode.put("type", type);
subNode.set("value", convertValueForKapacitor(mapper));
varsNode.set(name, subNode);
}

private JsonNode convertValueForKapacitor(ObjectMapper mapper) {
if (DURATION_TYPE.equals(type) && value instanceof String) {
Duration duration = DurationStyle.SIMPLE.parse((String) value);
return mapper.valueToTree(duration.toNanos());
} else {
return mapper.valueToTree(value);
}
}

public static TemplateVariable fromKapacitorResponse(String name, JsonNode variable) {
String type = variable.path("type").asText(null);
return TemplateVariable.builder()
Expand Down
Loading