Commit 5f99fa19 authored by Stefan Dähling's avatar Stefan Dähling
Browse files

add module liveness probe

parent 3945a56b
Pipeline #306146 failed with stages
in 3 minutes and 7 seconds
......@@ -135,17 +135,17 @@ func dummyClient(s *http.Server, t *testing.T) {
DF: false,
},
ImageGroups: []schemas.ImageGroupSpec{
schemas.ImageGroupSpec{
{
Config: schemas.ImageGroupConfig{
Image: "agent",
PullSecret: "",
},
Agents: []schemas.AgentSpec{
schemas.AgentSpec{
{
Name: "test1",
AType: "test",
},
schemas.AgentSpec{
{
Name: "test2",
AType: "test",
},
......
......@@ -67,6 +67,17 @@ var httpClient = &http.Client{Timeout: time.Second * 60}
var delay = time.Second * 1
var numRetries = 4
// Alive tests if alive
func Alive() (alive bool) {
alive = false
_, httpStatus, err := httpretry.Get(httpClient, "http://"+Host+":"+strconv.Itoa(Port)+
"/api/alive", time.Second*2, 2)
if err == nil && httpStatus == http.StatusOK {
alive = true
}
return
}
// GetCloneMAP requests CloneMAP information
func GetCloneMAP() (cmap schemas.CloneMAP, httpStatus int, err error) {
var body []byte
......
......@@ -71,6 +71,9 @@ func (ams *AMS) handleAPI(w http.ResponseWriter, r *http.Request) {
if respath[2] == "clonemap" {
cmapErr, httpErr = ams.handleCloneMAP(w, r)
resvalid = true
} else if respath[2] == "alive" {
cmapErr, httpErr = ams.handleAlive(w, r)
resvalid = true
}
case 4:
if respath[2] == "clonemap" && respath[3] == "mas" {
......@@ -160,6 +163,17 @@ func (ams *AMS) handleAPI(w http.ResponseWriter, r *http.Request) {
}
}
// handleAlive is the handler for requests to path /api/alive
func (ams *AMS) handleAlive(w http.ResponseWriter, r *http.Request) (cmapErr, httpErr error) {
if r.Method == "GET" {
httpErr = httpreply.Alive(w, nil)
} else {
httpErr = httpreply.MethodNotAllowed(w)
cmapErr = errors.New("Error: Method not allowed on path /api/alive")
}
return
}
// handleCloneMAP is the handler for requests to path /api/clonemap
func (ams *AMS) handleCloneMAP(w http.ResponseWriter, r *http.Request) (cmapErr, httpErr error) {
if r.Method == "GET" {
......
......@@ -252,7 +252,7 @@ func (kube *kubeDeployment) createHeadlessService(masID int) (err error) {
"app": "mas" + strconv.Itoa(masID) + "agencies",
},
Ports: []apicorev1.ServicePort{
apicorev1.ServicePort{
{
Port: 10000,
Name: "mas" + strconv.Itoa(masID) + "agencies",
},
......@@ -271,30 +271,30 @@ func (kube *kubeDeployment) createStatefulSet(masID int, imID int, image string,
// Pod Spec
podSpec := apicorev1.PodSpec{
Containers: []apicorev1.Container{
apicorev1.Container{
{
Name: "mas" + strconv.Itoa(masID) + "agencies",
Image: image,
ImagePullPolicy: "Always",
Ports: []apicorev1.ContainerPort{
apicorev1.ContainerPort{
{
ContainerPort: 10000,
Name: "mas" + strconv.Itoa(masID) + "agencies",
},
},
Env: []apicorev1.EnvVar{
apicorev1.EnvVar{
{
Name: "CLONEMAP_LOGGING",
Value: loggingEnv,
},
apicorev1.EnvVar{
{
Name: "CLONEMAP_MQTT",
Value: mqttEnv,
},
apicorev1.EnvVar{
{
Name: "CLONEMAP_DF",
Value: dfEnv,
},
apicorev1.EnvVar{
{
Name: "CLONEMAP_LOG_LEVEL",
Value: os.Getenv("CLONEMAP_LOG_LEVEL"),
},
......@@ -343,7 +343,7 @@ func (kube *kubeDeployment) createStatefulSet(masID int, imID int, image string,
}
if pullSecret != "" {
podSpec.ImagePullSecrets = []apicorev1.LocalObjectReference{
apicorev1.LocalObjectReference{
{
Name: pullSecret,
},
}
......
......@@ -112,6 +112,18 @@ func Resource(w http.ResponseWriter, v interface{}, cmaperr error) (err error) {
return
}
// Alive is standard response for liveness probe
func Alive(w http.ResponseWriter, cmaperr error) (err error) {
if cmaperr == nil {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
_, err = w.Write([]byte("I am alive"))
} else {
err = CMAPError(w, cmaperr.Error())
}
return
}
// JSONMarshalError writes standard response for JSON Marshal Error
func JSONMarshalError(w http.ResponseWriter) (err error) {
w.Header().Set("Content-Type", "text/plain")
......
......@@ -69,6 +69,17 @@ var httpClient = &http.Client{Timeout: time.Second * 60}
var delay = time.Second * 1
var numRetries = 4
// Alive tests if alive
func Alive() (alive bool) {
alive = false
_, httpStatus, err := httpretry.Get(httpClient, "http://"+Host+":"+strconv.Itoa(Port)+
"/api/alive", time.Second*2, 2)
if err == nil && httpStatus == http.StatusOK {
alive = true
}
return
}
// PostSvc post an mas
func PostSvc(masID int, svc schemas.Service) (retSvc schemas.Service, httpStatus int, err error) {
var body []byte
......@@ -123,6 +134,18 @@ func PostGraph(masID int, gr schemas.Graph) (httpStatus int, err error) {
return
}
// GetGraph returns graph of mas
func GetGraph(masID int) (graph schemas.Graph, httpStatus int, err error) {
var body []byte
body, httpStatus, err = httpretry.Get(httpClient, "http://"+Host+":"+strconv.Itoa(Port)+
"/api/df/"+strconv.Itoa(masID)+"/graph", time.Second*2, 2)
if err != nil {
return
}
err = json.Unmarshal(body, &graph)
return
}
// Init initializes the client
func Init(timeout time.Duration, del time.Duration, numRet int) {
httpClient.Timeout = timeout
......
......@@ -65,6 +65,11 @@ func (df *DF) handleAPI(w http.ResponseWriter, r *http.Request) {
resvalid := false
switch len(respath) {
case 3:
if respath[2] == "alive" {
cmapErr, httpErr = df.handleAlive(w, r)
resvalid = true
}
case 5:
var masID int
masID, cmapErr = strconv.Atoi(respath[3])
......@@ -124,6 +129,17 @@ func (df *DF) handleAPI(w http.ResponseWriter, r *http.Request) {
}
}
// handleAlive is the handler for requests to path /api/alive
func (df *DF) handleAlive(w http.ResponseWriter, r *http.Request) (cmapErr, httpErr error) {
if r.Method == "GET" {
httpErr = httpreply.Alive(w, nil)
} else {
httpErr = httpreply.MethodNotAllowed(w)
cmapErr = errors.New("Error: Method not allowed on path /api/alive")
}
return
}
// handlemasID is the handler for requests to path /api/df/{mas-id}/svc
func (df *DF) handlemasID(masID int, w http.ResponseWriter,
r *http.Request) (cmapErr, httpErr error) {
......
......@@ -47,6 +47,12 @@ package frontend
import (
"errors"
"net/http"
amsclient "git.rwth-aachen.de/acs/public/cloud/mas/clonemap/pkg/ams/client"
"git.rwth-aachen.de/acs/public/cloud/mas/clonemap/pkg/common/httpreply"
dfclient "git.rwth-aachen.de/acs/public/cloud/mas/clonemap/pkg/df/client"
logclient "git.rwth-aachen.de/acs/public/cloud/mas/clonemap/pkg/logger/client"
"git.rwth-aachen.de/acs/public/cloud/mas/clonemap/pkg/schemas"
)
// handlePlatform handles requests to /api/pf/...
......@@ -56,6 +62,7 @@ func (fe *Frontend) handlePlatform(w http.ResponseWriter, r *http.Request,
switch len(respath) {
case 4:
if respath[3] == "modules" {
cmapErr, httpErr = fe.handleModules(w, r)
resvalid = true
}
default:
......@@ -63,3 +70,29 @@ func (fe *Frontend) handlePlatform(w http.ResponseWriter, r *http.Request,
}
return
}
// handleModules is the handler for requests to path /api/pf/modules
func (fe *Frontend) handleModules(w http.ResponseWriter, r *http.Request) (cmapErr, httpErr error) {
if r.Method == "GET" {
// return short info of all MAS
var mods schemas.ModuleStatus
mods, cmapErr = getModuleStatus()
if cmapErr == nil {
httpErr = httpreply.Resource(w, mods, cmapErr)
} else {
httpErr = httpreply.CMAPError(w, cmapErr.Error())
}
} else {
httpErr = httpreply.MethodNotAllowed(w)
cmapErr = errors.New("Error: Method not allowed on path /api/pf/modules")
}
return
}
// getModuleStatus returns the on/off status of all modules
func getModuleStatus() (mods schemas.ModuleStatus, err error) {
mods.Logging = logclient.Alive()
mods.Core = amsclient.Alive()
mods.DF = dfclient.Alive()
return
}
......@@ -65,6 +65,17 @@ var httpClient = &http.Client{Timeout: time.Second * 60}
var delay = time.Second * 1
var numRetries = 4
// Alive tests if alive
func Alive() (alive bool) {
alive = false
_, httpStatus, err := httpretry.Get(httpClient, "http://"+Host+":"+strconv.Itoa(Port)+
"/api/alive", time.Second*2, 2)
if err == nil && httpStatus == http.StatusOK {
alive = true
}
return
}
// PostLogs posts new log messages to logger
func PostLogs(masID int, logs []schemas.LogMessage) (httpStatus int, err error) {
js, _ := json.Marshal(logs)
......@@ -73,6 +84,23 @@ func PostLogs(masID int, logs []schemas.LogMessage) (httpStatus int, err error)
return
}
// GetLatestLogs gets log messages
func GetLatestLogs(masID int, agentID int, topic string, num int) (msgs []schemas.LogMessage,
httpStatus int, err error) {
var body []byte
body, httpStatus, err = httpretry.Get(httpClient, "http://"+Host+":"+strconv.Itoa(Port)+
"/api/logging/"+strconv.Itoa(masID)+"/"+strconv.Itoa(agentID)+"/"+topic+"/latest/"+
strconv.Itoa(num), time.Second*2, 4)
if err != nil {
return
}
err = json.Unmarshal(body, &msgs)
if err != nil {
msgs = []schemas.LogMessage{}
}
return
}
// PutState updates the state
func PutState(state schemas.State) (httpStatus int, err error) {
js, _ := json.Marshal(state)
......
......@@ -68,6 +68,11 @@ func (logger *Logger) handleAPI(w http.ResponseWriter, r *http.Request) {
resvalid := false
switch len(respath) {
case 3:
if respath[2] == "alive" {
cmapErr, httpErr = logger.handleAlive(w, r)
resvalid = true
}
case 5:
if respath[2] == "logging" {
var masID int
......@@ -162,6 +167,17 @@ func (logger *Logger) handleAPI(w http.ResponseWriter, r *http.Request) {
}
}
// handleAlive is the handler for requests to path /api/alive
func (logger *Logger) handleAlive(w http.ResponseWriter, r *http.Request) (cmapErr, httpErr error) {
if r.Method == "GET" {
httpErr = httpreply.Alive(w, nil)
} else {
httpErr = httpreply.MethodNotAllowed(w)
cmapErr = errors.New("Error: Method not allowed on path /api/alive")
}
return
}
// handleLoggerNew is the handler for requests to path /api/logger/{mas-id}/{agent-id}/{logtype}
func (logger *Logger) handleLoggerNew(masID int, agentid int, logType string, w http.ResponseWriter,
r *http.Request) (cmapErr, httpErr error) {
......
......@@ -56,13 +56,20 @@ type CloneMAP struct {
Uptime time.Time `json:"uptime,omitempty"` // uptime of clonemap instance
}
// ModuleStatus shows the status of clonemaps modules
type ModuleStatus struct {
Core bool `json:"core"` // Core module
DF bool `json:"df"` // DF module
Logging bool `json:"logging"` // Logging module
}
// MASInfoShort contains info about MAS spec, agents in MAS
type MASInfoShort struct {
ID int `json:"id"`
Config MASConfig `json:"config"`
NumAgents int `json:"numags"`
Uptime time.Time `json:"uptime"`
Status Status `json:"status"`
ID int `json:"id"`
Config MASConfig `json:"config"`
NumAgents int `json:"numags"`
Uptime time.Time `json:"uptime"`
Status Status `json:"status"`
}
// MASInfo contains info about MAS spec, agents and agencies in MAS
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment