Skip to content

Commit

Permalink
feat: move to a more generic health endpoint in conjunction with isle…
Browse files Browse the repository at this point in the history
…ader check
  • Loading branch information
nenkoru committed Dec 10, 2024
1 parent d38ee19 commit 7aaba6b
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 115 deletions.
2 changes: 1 addition & 1 deletion controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func (c *Controller) initWeb() {
logrus.WithError(err).Fatalf("failed to create single page application factory")
}

if err = c.xweb.GetRegistry().Add(webapis.NewControllerIsLeaderApiFactory(c.env)); err != nil {
if err = c.xweb.GetRegistry().Add(webapis.NewControllerHealthCheckApiFactory(c.env, healthChecker)); err != nil {
logrus.WithError(err).Fatalf("failed to create controller-is-leader api factory")
}

Expand Down
142 changes: 142 additions & 0 deletions controller/webapis/controller-health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
Copyright NetFoundry 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
https://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 webapis

import (
"encoding/json"
"fmt"
gosundheit "github.com/AppsFlyer/go-sundheit"
"github.com/openziti/xweb/v2"
"github.com/openziti/ziti/controller/env"
"github.com/sirupsen/logrus"
"net/http"
"strings"
"time"
)

var _ xweb.ApiHandlerFactory = &ControllerHealthCheckApiFactory{}

type ControllerHealthCheckApiFactory struct {
appEnv *env.AppEnv
healthChecker gosundheit.Health
}

func (factory ControllerHealthCheckApiFactory) Validate(config *xweb.InstanceConfig) error {
return nil
}

func NewControllerHealthCheckApiFactory(appEnv *env.AppEnv, healthChecker gosundheit.Health) *ControllerHealthCheckApiFactory {
return &ControllerHealthCheckApiFactory{
appEnv: appEnv,
healthChecker: healthChecker,
}
}

func (factory ControllerHealthCheckApiFactory) Binding() string {
return ControllerHealthCheckApiBinding
}

func (factory ControllerHealthCheckApiFactory) New(_ *xweb.ServerConfig, options map[interface{}]interface{}) (xweb.ApiHandler, error) {
return &ControllerHealthCheckApiHandler{
healthChecker: factory.healthChecker,
appEnv: factory.appEnv,
options: options,
}, nil

}

type ControllerHealthCheckApiHandler struct {
handler http.Handler
options map[interface{}]interface{}
appEnv *env.AppEnv
healthChecker gosundheit.Health
}

func (self ControllerHealthCheckApiHandler) Binding() string {
return ControllerHealthCheckApiBinding
}

func (self ControllerHealthCheckApiHandler) Options() map[interface{}]interface{} {
return self.options
}

func (self ControllerHealthCheckApiHandler) RootPath() string {
return "/controller/health"
}

func (self ControllerHealthCheckApiHandler) IsHandler(r *http.Request) bool {
return strings.HasPrefix(r.URL.Path, self.RootPath())
}

func (self *ControllerHealthCheckApiHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) {
output := map[string]interface{}{}
output["meta"] = map[string]interface{}{}

data := map[string]interface{}{}
output["data"] = data

w.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")

results, healthy := self.healthChecker.Results()
isLeader := self.appEnv.GetHostController().IsRaftLeader()
isRaftEnabled := self.appEnv.GetHostController().IsRaftEnabled()
raftData := map[string]interface{}{}
data["healthy"] = healthy
raftData["isLeader"] = isLeader
raftData["isRaftEnabled"] = isRaftEnabled
output["raft"] = raftData
var checks []map[string]interface{}
shortFormat := request.URL.Query().Get("type") == "short"

for id, result := range results {
check := map[string]interface{}{}
checks = append(checks, check)
check["id"] = id
check["healthy"] = result.IsHealthy()
if !shortFormat {
check["lastCheckDuration"] = fmt.Sprintf("%v", result.Duration)
check["lastCheckTime"] = result.Timestamp.UTC().Format(time.RFC3339)

if result.Error != nil {
check["err"] = result.Error
check["consecutiveFailures"] = result.ContiguousFailures
}

if result.TimeOfFirstFailure != nil {
check["failingSince"] = result.TimeOfFirstFailure.UTC().Format(time.RFC3339)
}
if result.Details != "didn't run yet" {
check["details"] = result.Details
}
}
}
data["checks"] = checks

if !isLeader && isRaftEnabled {
w.WriteHeader(429)
}

if err := encoder.Encode(output); err != nil {
logrus.WithError(err).Error("failure encoding health check results")
}
}

func (self ControllerHealthCheckApiHandler) IsDefault() bool {
return false
}
102 changes: 0 additions & 102 deletions controller/webapis/controller-isleader.go

This file was deleted.

24 changes: 12 additions & 12 deletions controller/webapis/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,24 @@ const (
RestApiRootPath = "/edge"
ClientRestApiBase = "/edge/client"
ManagementRestApiBase = "/edge/management"
ControllerIsLeader = "/sys/health"
ControllerHealthCheck = "/controller/health"

LegacyClientRestApiBaseUrlV1 = RestApiRootPath + RestApiV1
ClientRestApiBaseUrlV1 = ClientRestApiBase + RestApiV1
ManagementRestApiBaseUrlV1 = ManagementRestApiBase + RestApiV1
ControllerIsLeaderApiBaseUrlV1 = ControllerIsLeader + RestApiV1
LegacyClientRestApiBaseUrlV1 = RestApiRootPath + RestApiV1
ClientRestApiBaseUrlV1 = ClientRestApiBase + RestApiV1
ManagementRestApiBaseUrlV1 = ManagementRestApiBase + RestApiV1
ControllerHealthCheckApiBaseUrlV1 = ControllerHealthCheck + RestApiV1

ClientRestApiBaseUrlLatest = ClientRestApiBaseUrlV1
ManagementRestApiBaseUrlLatest = ManagementRestApiBaseUrlV1

ClientRestApiSpecUrl = ClientRestApiBaseUrlLatest + "/swagger.json"
ManagementRestApiSpecUrl = ManagementRestApiBaseUrlLatest + "/swagger.json"

LegacyClientApiBinding = "edge"
ClientApiBinding = "edge-client"
ManagementApiBinding = "edge-management"
OidcApiBinding = "edge-oidc"
ControllerIsLeaderApiBinding = "controller-isleader"
LegacyClientApiBinding = "edge"
ClientApiBinding = "edge-client"
ManagementApiBinding = "edge-management"
OidcApiBinding = "edge-oidc"
ControllerHealthCheckApiBinding = "controller-health"
)

// AllApiBindingVersions is a map of: API Binding -> Api Version -> API Path
Expand All @@ -53,7 +53,7 @@ var AllApiBindingVersions = map[string]map[string]string{
ManagementApiBinding: {
VersionV1: ManagementRestApiBaseUrlV1,
},
ControllerIsLeaderApiBinding: {
VersionV1: ControllerIsLeaderApiBaseUrlV1,
ControllerHealthCheckApiBinding: {
VersionV1: ControllerHealthCheckApiBaseUrlV1,
},
}

0 comments on commit 7aaba6b

Please sign in to comment.