-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathmanagement_api.go
More file actions
155 lines (134 loc) · 5.21 KB
/
management_api.go
File metadata and controls
155 lines (134 loc) · 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package main
import (
"bytes"
_ "embed"
"encoding/json"
"log"
"net/http"
"time"
)
// handleStatus handles the /status endpoint request
func handleStatus(responseWriter http.ResponseWriter, request *http.Request, services []ServiceConfig) {
// ServiceStatus represents the current state of a service
type ServiceStatus struct {
Name string `json:"name"`
ListenPort string `json:"listen_port"`
IsRunning bool `json:"is_running"`
ActiveConnections int `json:"active_connections"`
LastUsed *time.Time `json:"last_used"`
ServiceUrl *string `json:"service_url,omitempty"`
ResourceRequirements map[string]int `json:"resource_requirements"`
}
// ResourceUsage represents the current usage of a resource
type ResourceUsage struct {
TotalAvailable int `json:"total_available"`
TotalInUse int `json:"total_in_use"`
UsageByService map[string]int `json:"usage_by_service"`
}
// StatusResponse represents the complete status response
type StatusResponse struct {
Services []ServiceStatus `json:"services"`
Resources map[string]ResourceUsage `json:"resources"`
}
if request.Method != "GET" {
http.Error(responseWriter, "Only GET requests allowed", http.StatusMethodNotAllowed)
return
}
responseWriter.Header().Set("Content-Type", "application/json; charset=utf-8")
// Lock to safely access resource manager state
resourceManager.serviceMutex.Lock()
defer resourceManager.serviceMutex.Unlock()
response := StatusResponse{
Services: make([]ServiceStatus, 0, len(services)),
Resources: make(map[string]ResourceUsage),
}
// Initialize resource usage tracking
for resource := range config.ResourcesAvailable {
response.Resources[resource] = ResourceUsage{
TotalAvailable: *resourceManager.resourcesAvailable[resource],
TotalInUse: resourceManager.resourcesInUse[resource],
UsageByService: make(map[string]int),
}
}
// Process all services
for _, service := range services {
status := ServiceStatus{
Name: service.Name,
ListenPort: service.ListenPort,
ResourceRequirements: service.ResourceRequirements,
}
// Determine service URL template to use
urlTemplate, err := service.GetServiceUrlTemplate(config.DefaultServiceUrl)
if err != nil {
log.Printf("[Management API] Failed to get service URL template for service %s: %v", service.Name, err)
}
// Render service URL if template is available
if urlTemplate != nil && service.ListenPort != "" {
var buf bytes.Buffer
data := map[string]string{"PORT": service.ListenPort}
err := urlTemplate.Execute(&buf, data)
if err != nil {
log.Printf("[Management API] Failed to render service URL template for service %s: %v", service.Name, err)
} else {
renderedUrl := buf.String()
status.ServiceUrl = &renderedUrl
}
}
// Check if service is running
if runningService, ok := resourceManager.runningServices[service.Name]; ok {
status.IsRunning = true
status.ActiveConnections = runningService.activeConnections
status.LastUsed = runningService.lastUsed
// Update resource usage by service
for resource, amount := range service.ResourceRequirements {
if usage, ok := response.Resources[resource]; ok {
usage.UsageByService[service.Name] = amount
response.Resources[resource] = usage
}
}
}
response.Services = append(response.Services, status)
}
// Encode and send response
if err := json.NewEncoder(responseWriter).Encode(response); err != nil {
http.Error(responseWriter, "{error: \"Failed to produce JSON response\"}", http.StatusInternalServerError)
log.Printf("[Management API] Failed to produce /status JSON response: %s\n", err.Error())
}
}
//go:embed management-ui/index.html
var uiIndexContents []byte
// startManagementApi starts the management API on the specified port
func startManagementApi(managementAPI ManagementApi, services []ServiceConfig) {
mux := http.NewServeMux()
mux.HandleFunc("/status", func(responseWriter http.ResponseWriter, request *http.Request) {
printRequestUrlManagementApi(request)
handleStatus(responseWriter, request, services)
})
mux.HandleFunc("/", func(responseWriter http.ResponseWriter, request *http.Request) {
printRequestUrlManagementApi(request)
if request.URL.Path != "/" {
http.NotFound(responseWriter, request)
return
}
responseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
responseWriter.WriteHeader(http.StatusOK)
bytesWritten, err := responseWriter.Write(uiIndexContents)
if err != nil {
log.Printf("[Management API] Failed to send UI index page: %s\n", err.Error())
}
if bytesWritten != len(uiIndexContents) {
log.Printf("[Management API] Incomplete index page written: %s\n", err.Error())
}
})
server := &http.Server{
Addr: ":" + managementAPI.ListenPort,
Handler: mux,
}
log.Printf("[Management API] Listening on port %s", managementAPI.ListenPort)
if err := server.ListenAndServe(); err != nil {
log.Fatalf("[Management API] Could not start management API: %s\n", err.Error())
}
}
func printRequestUrlManagementApi(request *http.Request) {
log.Printf("[Management API] %s %s", request.Method, request.URL)
}