Skip to content

Commit

Permalink
Merge pull request #640 from haoming29/fix-servertype-string
Browse files Browse the repository at this point in the history
Improved common Web API endpoint
  • Loading branch information
haoming29 authored Jan 17, 2024
2 parents 9d0c2b9 + 305626c commit 8b68603
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 51 deletions.
49 changes: 32 additions & 17 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"os"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -179,11 +180,42 @@ func IsServerEnabled(testServer ServerType) bool {
return enabledServers.IsEnabled(testServer)
}

// Get a string slice of currently enabled servers, sorted by alphabetical order.
// By default, it calls String method of each enabled server.
// To get strings in lowerCase, set lowerCase = true.
func GetEnabledServerString(lowerCase bool) []string {
servers := make([]string, 0)
if enabledServers.IsEnabled(CacheType) {
servers = append(servers, CacheType.String())
}
if enabledServers.IsEnabled(OriginType) {
servers = append(servers, OriginType.String())
}
if enabledServers.IsEnabled(DirectorType) {
servers = append(servers, DirectorType.String())
}
if enabledServers.IsEnabled(RegistryType) {
servers = append(servers, RegistryType.String())
}
sort.Strings(servers)
if lowerCase {
for i, serverStr := range servers {
servers[i] = strings.ToLower(serverStr)
}
return servers
} else {
return servers
}
}

// Create a new, empty ServerType bitmask
func NewServerType() ServerType {
return ServerType(0)
}

// Get the string representation of a ServerType instance. This is intended
// for getting the string form of a single ServerType contant, such as CacheType
// OriginType, etc. To get a string slice of enabled servers, use EnabledServerString()
func (sType ServerType) String() string {
switch sType {
case CacheType:
Expand All @@ -198,23 +230,6 @@ func (sType ServerType) String() string {
return "Unknown"
}

func EnabledServers() []string {
servers := make([]string, 0)
if enabledServers.IsEnabled(CacheType) {
servers = append(servers, "cache")
}
if enabledServers.IsEnabled(OriginType) {
servers = append(servers, "origin")
}
if enabledServers.IsEnabled(DirectorType) {
servers = append(servers, "director")
}
if enabledServers.IsEnabled(RegistryType) {
servers = append(servers, "registry")
}
return servers
}

func (sType *ServerType) SetString(name string) bool {
switch strings.ToLower(name) {
case "cache":
Expand Down
29 changes: 29 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"sort"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -191,6 +192,15 @@ func TestDeprecateLogMessage(t *testing.T) {

func TestEnabledServers(t *testing.T) {
allServerTypes := []ServerType{OriginType, CacheType, DirectorType, RegistryType}
allServerStrs := make([]string, 0)
allServerStrsLower := make([]string, 0)
for _, st := range allServerTypes {
allServerStrs = append(allServerStrs, st.String())
allServerStrsLower = append(allServerStrsLower, strings.ToLower(st.String()))
}
sort.Strings(allServerStrs)
sort.Strings(allServerStrsLower)

t.Run("no-value-set", func(t *testing.T) {
enabledServers = 0
for _, server := range allServerTypes {
Expand All @@ -204,14 +214,33 @@ func TestEnabledServers(t *testing.T) {
// We didn't call setEnabledServer as it will only set once per process
enabledServers.SetList([]ServerType{server})
assert.True(t, IsServerEnabled(server))
assert.Equal(t, []string{server.String()}, GetEnabledServerString(false))
assert.Equal(t, []string{strings.ToLower(server.String())}, GetEnabledServerString(true))
}
})

t.Run("enable-multiple-servers", func(t *testing.T) {
enabledServers = 0
enabledServers.SetList([]ServerType{OriginType, CacheType})
serverStr := []string{OriginType.String(), CacheType.String()}
serverStrLower := []string{strings.ToLower(OriginType.String()), strings.ToLower(CacheType.String())}
sort.Strings(serverStr)
sort.Strings(serverStrLower)
assert.True(t, IsServerEnabled(OriginType))
assert.True(t, IsServerEnabled(CacheType))
assert.Equal(t, serverStr, GetEnabledServerString(false))
assert.Equal(t, serverStrLower, GetEnabledServerString(true))
})

t.Run("enable-all-servers", func(t *testing.T) {
enabledServers = 0
enabledServers.SetList(allServerTypes)
assert.True(t, IsServerEnabled(OriginType))
assert.True(t, IsServerEnabled(CacheType))
assert.True(t, IsServerEnabled(RegistryType))
assert.True(t, IsServerEnabled(DirectorType))
assert.Equal(t, allServerStrs, GetEnabledServerString(false))
assert.Equal(t, allServerStrsLower, GetEnabledServerString(true))
})

t.Run("setEnabledServer-only-set-once", func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion launchers/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func LaunchModules(ctx context.Context, modules config.ServerType) (context.Canc
return nil
})

if err = server_utils.WaitUntilWorking(ctx, "GET", param.Server_ExternalWebUrl.GetString()+"/api/v1.0/servers", "Web UI", http.StatusOK); err != nil {
if err = server_utils.WaitUntilWorking(ctx, "GET", param.Server_ExternalWebUrl.GetString()+"/api/v1.0/health", "Web UI", http.StatusOK); err != nil {
log.Errorln("Web engine startup appears to have failed:", err)
return shutdownCancel, err
}
Expand Down
1 change: 1 addition & 0 deletions param/parameters_struct.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion web_ui/frontend/app/(landing)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function Home() {
try {
const res = await fetch("/api/v1.0/servers")
const data = await res.json()
setEnabledServers(data)
setEnabledServers(data?.servers)
} catch {
setEnabledServers(["origin", "director", "registry"])
}
Expand Down
2 changes: 1 addition & 1 deletion web_ui/frontend/app/(login)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function Home() {
const response = await fetch("/api/v1.0/servers")
if (response.ok) {
const data = await response.json()
setEnabledServers(data)
setEnabledServers(data?.servers)
}
})()
}, []);
Expand Down
70 changes: 48 additions & 22 deletions web_ui/frontend/app/api/docs/pelican-swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -247,38 +247,27 @@ tags:
description: Authentication APIs for all servers
- name: common
description: Common APIs for all servers
- name: metrics
description: APIs for various server metrics
- name: registry_ui
description: APIs for Registry server Web UI
paths:
/health:
get:
tags:
- common
summary: Returns the health status of server components
description: "`Authentication Required`"
- "common"
summary: Health check endpoint for server Web engine
produces:
- application/json
responses:
"200":
description: OK
description: "Server Web engine is running and taking requests"
schema:
type: object
properties:
status:
message:
type: string
description: The overall health status of the server
components:
type: object
description: The health status of each server components
properties:
cmsd:
$ref: "#/definitions/HealthStatus"
federation:
$ref: "#/definitions/HealthStatus"
web-ui:
$ref: "#/definitions/HealthStatus"
xrootd:
$ref: "#/definitions/HealthStatus"
example: "Web Engine Running. Time: 2024-01-10 22:32:59.637471175 +0000 UTC m=+35.515010725"
/config:
get:
tags:
Expand Down Expand Up @@ -332,21 +321,58 @@ paths:
tags:
- common
summary: Returns a list of enabled servers
description: "`Authentication Required`
Server names are in lower-case, sorted in alphabetical order.
"
produces:
- application/json
responses:
"200":
description: OK
schema:
type: array
items:
type: string
minItems: 1
type: object
properties:
servers:
type: array
items:
type: string
minItems: 1
example: ["director", "origin", "registry"]
"500":
description: Server encountered error in reading institution configuration
schema:
type: object
$ref: "#/definitions/ErrorModel"
/metrics/health:
get:
tags:
- metrics
summary: Returns the health status of server components
description: "`Authentication Required`"
produces:
- application/json
responses:
"200":
description: OK
schema:
type: object
properties:
status:
type: string
description: The overall health status of the server
components:
type: object
description: The health status of each server components
properties:
cmsd:
$ref: "#/definitions/HealthStatus"
federation:
$ref: "#/definitions/HealthStatus"
web-ui:
$ref: "#/definitions/HealthStatus"
xrootd:
$ref: "#/definitions/HealthStatus"
/auth/login:
post:
tags:
Expand Down
2 changes: 1 addition & 1 deletion web_ui/frontend/app/config/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ export default function Config() {
try {
const res = await fetch("/api/v1.0/servers")
const data = await res.json()
setEnabledServers(data)
setEnabledServers(data?.servers)
} catch {
setEnabledServers(["origin", "director", "registry"])
}
Expand Down
2 changes: 1 addition & 1 deletion web_ui/frontend/components/StatusBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default function StatusBox() {
const [error, setError] = useState<string | undefined>(undefined)

let getStatus = async () => {
let response = await fetch("/api/v1.0/health")
let response = await fetch("/api/v1.0/metrics/health")

if(response.ok) {
let data = await response.json()
Expand Down
17 changes: 10 additions & 7 deletions web_ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ func getConfigValues(ctx *gin.Context) {
}

func getEnabledServers(ctx *gin.Context) {
enabledServers := config.EnabledServers()
enabledServers := config.GetEnabledServerString(true)
if len(enabledServers) == 0 {
ctx.JSON(500, gin.H{"error": "No enabled servers found"})
return
}

ctx.JSON(200, enabledServers)
ctx.JSON(200, gin.H{"servers": enabledServers})
}

func configureWebResource(engine *gin.Engine) error {
Expand Down Expand Up @@ -136,8 +136,8 @@ func configureWebResource(engine *gin.Engine) error {
}

// If just one server is enabled, redirect to that server
if len(config.EnabledServers()) == 1 && path == "/index.html" {
ctx.Redirect(http.StatusFound, "/view/"+config.EnabledServers()[0]+"/index.html")
if len(config.GetEnabledServerString(true)) == 1 && path == "/index.html" {
ctx.Redirect(http.StatusFound, "/view/"+config.GetEnabledServerString(true)[0]+"/index.html")
return
}

Expand Down Expand Up @@ -167,8 +167,11 @@ func configureWebResource(engine *gin.Engine) error {
// Configure common endpoint available to all server web UI which are located at /api/v1.0/*
func configureCommonEndpoints(engine *gin.Engine) error {
engine.GET("/api/v1.0/config", AuthHandler, getConfigValues)
engine.GET("/api/v1.0/servers", getEnabledServers)

engine.GET("/api/v1.0/servers", AuthHandler, getEnabledServers)
// Health check endpoint for web engine
engine.GET("/api/v1.0/health", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Web Engine Running. Time: %s", time.Now().String())})
})
return nil
}

Expand All @@ -180,7 +183,7 @@ func configureMetrics(ctx context.Context, engine *gin.Engine) error {
prometheusMonitor := ginprometheus.NewPrometheus("gin")
prometheusMonitor.Use(engine)

engine.GET("/api/v1.0/health", AuthHandler, func(ctx *gin.Context) {
engine.GET("/api/v1.0/metrics/health", AuthHandler, func(ctx *gin.Context) {
healthStatus := metrics.GetHealthStatus()
ctx.JSON(http.StatusOK, healthStatus)
})
Expand Down

0 comments on commit 8b68603

Please sign in to comment.