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

Enable app health checks #493

Merged
merged 2 commits into from
Dec 17, 2019
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
76 changes: 76 additions & 0 deletions service-base/src/main/java/org/oskari/status/AppStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.oskari.status;

import fi.nls.oskari.service.OskariComponent;

/**
* Get status with:
* OskariComponentManager.getComponentsOfType(AppStatus.class)
*
* Usage example:
boolean highSeverityChecksOk = getChecks().stream()
.filter(s -> s.isEnabled())
.filter(s -> s.getSeverity() == AppStatus.Severity.HIGH)
.allMatch(s -> s.isOk());
*/
public abstract class AppStatus extends OskariComponent {

public enum Severity {
// allow app to keep running
LOW,
// app should stop responding if this is not OK
HIGH
};

public enum Level {
// all good
OK,
// parts of the feature is working while others are not
PARTIAL,
// not working at all
BROKEN
};

/**
* Name for the check that could be shown in a status monitor
* @return
*/
public String getName() {
return this.getClass().getName();
}

public boolean isEnabled() {
return true;
}
/**
* Describe what is being checked
* @return
*/
public String getDescription() {
return "";
}

/**
* Provide reason for check failing
* @return
*/
public String getReason() {
return "";
}

/**
* LOW if the app can run even without this feature working
* HIGH if the app should be stopped when this functionality is not working
* @return
*/
public abstract Severity getSeverity();

/**
* Level of functionality: ok, partial, broken == not working
* @return
*/
public abstract Level getLevel();

public boolean isOk() {
return Level.OK.equals(getLevel());
}
}
55 changes: 55 additions & 0 deletions servlet-map/src/main/java/fi/nls/oskari/ForceDisableByFile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package fi.nls.oskari;

import fi.nls.oskari.annotation.Oskari;
import fi.nls.oskari.service.OskariComponent;
import fi.nls.oskari.util.PropertyUtil;
import org.oskari.status.AppStatus;

import java.io.File;

@Oskari
public class ForceDisableByFile extends AppStatus {

private String fileToCheck;

public void init() {
fileToCheck = PropertyUtil.get("disablefile.path", null);
}

public boolean isEnabled() {
return fileToCheck != null;
}

/**
* File found -> app should be disabled
* not found -> OK
* @return
*/
public Level getLevel() {
File status = new File(fileToCheck);
if(status.exists()) {
return Level.BROKEN;
}
return Level.OK;
}

public String getDescription() {
return "Checks if file '" + fileToCheck + "' exists.";
}

/**
* Provide reason for check failing
* @return
*/
public String getReason() {
return fileToCheck + " file exists";
}

/**
* This is a manual check for file. If it exists -> the app should be stopped
* @return
*/
public Severity getSeverity() {
return Severity.HIGH;
}
}
48 changes: 48 additions & 0 deletions servlet-map/src/main/java/fi/nls/oskari/StatusController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package fi.nls.oskari;

import fi.nls.oskari.control.ActionParameters;
import fi.nls.oskari.service.OskariComponentManager;
import fi.nls.oskari.spring.extension.OskariParam;
import org.oskari.status.AppStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Controller
public class StatusController {

private Collection<AppStatus> getChecks() {
Map<String, AppStatus> statuses = OskariComponentManager.getComponentsOfType(AppStatus.class);
return statuses.values();
}

@RequestMapping("/health")
public ResponseEntity<String> health() {
boolean highSeverityChecksOk = getChecks().stream()
.filter(s -> s.isEnabled())
.filter(s -> s.getSeverity() == AppStatus.Severity.HIGH)
.allMatch(s -> s.isOk());
if (!highSeverityChecksOk) {
return new ResponseEntity<String>("DISABLED", HttpStatus.SERVICE_UNAVAILABLE);
}
return new ResponseEntity("OK", HttpStatus.OK);
}

@RequestMapping("/status")
@ResponseBody
public Collection<AppStatus> status(@OskariParam ActionParameters params) {
if (!params.getUser().isAdmin()) {
return Collections.emptyList();
}
return getChecks();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fi.nls.oskari.annotation.OskariComponentAnnotationProcessor