Skip to content

Commit

Permalink
feat: add controller-isleader xweb component to return 200 if the con…
Browse files Browse the repository at this point in the history
…troller is a raft leader
  • Loading branch information
nenkoru committed Dec 9, 2024
1 parent 1932a6d commit d38ee19
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 7 deletions.
6 changes: 6 additions & 0 deletions controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,10 @@ 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 {
logrus.WithError(err).Fatalf("failed to create controller-is-leader api factory")
}

if c.IsEdgeEnabled() {
managementApiFactory := webapis.NewManagementApiFactory(c.env)
clientApiFactory := webapis.NewClientApiFactory(c.env)
Expand Down Expand Up @@ -663,6 +667,8 @@ func getApiPath(binding string) string {
return "health-checks"
case "edge-oidc":
return "/oidc"
case "controller-isleader":
return "/sys/health"
}

return ""
Expand Down
102 changes: 102 additions & 0 deletions controller/webapis/controller-isleader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
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"
"github.com/openziti/xweb/v2"
"github.com/openziti/ziti/controller/env"
"github.com/sirupsen/logrus"
"net/http"
"strings"
)

var _ xweb.ApiHandlerFactory = &ControllerIsLeaderApiFactory{}

type ControllerIsLeaderApiFactory struct {
appEnv *env.AppEnv
}

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

func NewControllerIsLeaderApiFactory(appEnv *env.AppEnv) *ControllerIsLeaderApiFactory {
return &ControllerIsLeaderApiFactory{
appEnv: appEnv,
}
}

func (factory ControllerIsLeaderApiFactory) Binding() string {
return ControllerIsLeaderApiBinding
}

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

}

type ControllerIsLeaderApiHandler struct {
handler http.Handler
options map[interface{}]interface{}
appEnv *env.AppEnv
}

func (self ControllerIsLeaderApiHandler) Binding() string {
return ControllerIsLeaderApiBinding
}

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

func (self ControllerIsLeaderApiHandler) RootPath() string {
return "/sys/health"
}

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

func (self *ControllerIsLeaderApiHandler) 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("", " ")

isLeader := self.appEnv.GetHostController().IsRaftLeader()
data["isLeader"] = isLeader
if !isLeader {
w.WriteHeader(429)
}

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

func (self ControllerIsLeaderApiHandler) IsDefault() bool {
return false
}
20 changes: 13 additions & 7 deletions controller/webapis/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,24 @@ const (
RestApiRootPath = "/edge"
ClientRestApiBase = "/edge/client"
ManagementRestApiBase = "/edge/management"
ControllerIsLeader = "/sys/health"

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

ClientRestApiBaseUrlLatest = ClientRestApiBaseUrlV1
ManagementRestApiBaseUrlLatest = ManagementRestApiBaseUrlV1

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

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

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

0 comments on commit d38ee19

Please sign in to comment.