Skip to content

Commit e5b5a2b

Browse files
committed
Added dynamic config update apis
1 parent 54a6bba commit e5b5a2b

File tree

6 files changed

+157
-6
lines changed

6 files changed

+157
-6
lines changed

cmd/openrun/server_cmds.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package main
55

66
import (
77
"context"
8+
"encoding/json"
89
"errors"
910
"fmt"
1011
"io"
@@ -44,6 +45,24 @@ func getServerCommands(serverConfig *types.ServerConfig, clientConfig *types.Cli
4445
return stopServer(cCtx, clientConfig)
4546
},
4647
},
48+
{
49+
Name: "show-config",
50+
Usage: "Show the server dynamic config",
51+
Flags: flags,
52+
Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewTomlSourceFromFlagFunc(configFileFlagName)),
53+
Action: func(cCtx *cli.Context) error {
54+
return showConfig(cCtx, clientConfig)
55+
},
56+
},
57+
{
58+
Name: "update-config",
59+
Usage: "Update the server dynamic config",
60+
Flags: flags,
61+
Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewTomlSourceFromFlagFunc(configFileFlagName)),
62+
Action: func(cCtx *cli.Context) error {
63+
return updateConfig(cCtx, clientConfig)
64+
},
65+
},
4766
},
4867
},
4968
}, nil
@@ -127,3 +146,49 @@ func stopServer(_ *cli.Context, clientConfig *types.ClientConfig) error {
127146
}
128147
return nil
129148
}
149+
150+
func showConfig(_ *cli.Context, clientConfig *types.ClientConfig) error {
151+
client := system.NewHttpClient(clientConfig.ServerUri, clientConfig.AdminUser, clientConfig.Client.AdminPassword, clientConfig.Client.SkipCertCheck)
152+
153+
var response types.ConfigResponse
154+
err := client.Get("/_openrun/config", nil, &response)
155+
if err != nil {
156+
return err
157+
}
158+
json, err := json.MarshalIndent(response.DynamicConfig, "", " ")
159+
if err != nil {
160+
return err
161+
}
162+
fmt.Printf("%s\n", string(json))
163+
return nil
164+
}
165+
166+
func updateConfig(cCtx *cli.Context, clientConfig *types.ClientConfig) error {
167+
client := system.NewHttpClient(clientConfig.ServerUri, clientConfig.AdminUser, clientConfig.Client.AdminPassword, clientConfig.Client.SkipCertCheck)
168+
169+
if cCtx.NArg() != 1 {
170+
return fmt.Errorf("expected one argument: <configFilePath>")
171+
}
172+
configFilePath := cCtx.Args().Get(0)
173+
configFile, err := os.ReadFile(configFilePath)
174+
if err != nil {
175+
return err
176+
}
177+
var inputConfig types.DynamicConfig
178+
err = json.Unmarshal(configFile, &inputConfig)
179+
if err != nil {
180+
return err
181+
}
182+
var response types.ConfigResponse
183+
err = client.Post("/_openrun/config", nil, &inputConfig, &response)
184+
if err != nil {
185+
return err
186+
}
187+
188+
json, err := json.MarshalIndent(response.DynamicConfig, "", " ")
189+
if err != nil {
190+
return err
191+
}
192+
fmt.Printf("%s\n", string(json))
193+
return nil
194+
}

internal/server/router.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,9 +1078,21 @@ func (h *Handler) listSyncEntries(r *http.Request) (any, error) {
10781078
return results, nil
10791079
}
10801080

1081+
func (h *Handler) configGet(r *http.Request) (any, error) {
1082+
return types.ConfigResponse{DynamicConfig: h.server.GetDynamicConfig()}, nil
1083+
}
1084+
10811085
func (h *Handler) configUpdate(r *http.Request) (any, error) {
1082-
// TODO
1083-
return nil, nil
1086+
var dynamicConfig types.DynamicConfig
1087+
err := json.NewDecoder(r.Body).Decode(&dynamicConfig)
1088+
if err != nil {
1089+
return nil, types.CreateRequestError(err.Error(), http.StatusBadRequest)
1090+
}
1091+
newConfig, err := h.server.UpdateDynamicConfig(r.Context(), &dynamicConfig)
1092+
if err != nil {
1093+
return nil, types.CreateRequestError(err.Error(), http.StatusBadRequest)
1094+
}
1095+
return types.ConfigResponse{DynamicConfig: *newConfig}, nil
10841096
}
10851097

10861098
// serveInternal returns a handler for the internal APIs for app admin and management
@@ -1208,7 +1220,12 @@ func (h *Handler) serveInternal(enableBasicAuth bool) http.Handler {
12081220
h.apiHandler(w, r, enableBasicAuth, "list_sync", h.listSyncEntries)
12091221
}))
12101222

1211-
// API to run sync
1223+
// API to get config
1224+
r.Get("/config", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1225+
h.apiHandler(w, r, enableBasicAuth, "config_get", h.configGet)
1226+
}))
1227+
1228+
// API to update config
12121229
r.Post("/config", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12131230
h.apiHandler(w, r, enableBasicAuth, "config_update", h.configUpdate)
12141231
}))

internal/server/server.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"crypto/x509"
1111
"database/sql"
1212
"embed"
13+
"encoding/json"
1314
"errors"
1415
"fmt"
1516
"log"
@@ -230,16 +231,58 @@ func NewServer(config *types.ServerConfig) (*Server, error) {
230231
// TODO : notify other instances about new dynamic config
231232
}
232233

234+
err = server.SaveDynamicConfig(context.Background())
235+
if err != nil {
236+
return nil, fmt.Errorf("error saving dynamic config: %w", err)
237+
}
238+
233239
// Start the idle shutdown check
234240
server.syncTimer = time.NewTicker(time.Minute) // run sync every minute
235241
go server.syncRunner()
236242
return server, nil
237243
}
238244

239-
func (s *Server) GetRBACConfig() types.RBACConfig {
245+
func (s *Server) GetDynamicConfig() types.DynamicConfig {
240246
s.configMu.RLock()
241247
defer s.configMu.RUnlock()
242-
return s.dynamicConfig.RBAC
248+
return *s.dynamicConfig // return a copy of the dynamic config
249+
}
250+
251+
func (s *Server) SaveDynamicConfig(ctx context.Context) error {
252+
targetPath := os.ExpandEnv("$OPENRUN_HOME/config/dynamic_config.json")
253+
configJson, err := json.MarshalIndent(s.dynamicConfig, "", " ")
254+
if err != nil {
255+
return fmt.Errorf("error marshalling dynamic config: %w", err)
256+
}
257+
err = os.WriteFile(targetPath, configJson, 0600)
258+
if err != nil {
259+
return fmt.Errorf("error writing dynamic config: %w", err)
260+
}
261+
return nil
262+
}
263+
264+
func (s *Server) UpdateDynamicConfig(ctx context.Context, newConfig *types.DynamicConfig) (*types.DynamicConfig, error) {
265+
s.configMu.Lock()
266+
defer s.configMu.Unlock()
267+
268+
currentVersionId := s.dynamicConfig.VersionId
269+
if currentVersionId != newConfig.VersionId {
270+
// stale update
271+
return nil, fmt.Errorf("config version id mismatch, expected %s, got %s", currentVersionId, newConfig.VersionId)
272+
}
273+
274+
newConfig.VersionId = "ver_" + ksuid.New().String()
275+
err := s.db.UpdateConfig(ctx, system.GetContextUserId(ctx), currentVersionId, newConfig)
276+
if err != nil {
277+
return nil, fmt.Errorf("error updating dynamic config: %w", err)
278+
}
279+
s.dynamicConfig = newConfig
280+
err = s.SaveDynamicConfig(ctx)
281+
if err != nil {
282+
return nil, fmt.Errorf("error saving dynamic config: %w", err)
283+
}
284+
//TODO: notify other instances about new dynamic config
285+
return newConfig, nil
243286
}
244287

245288
func (s *Server) appNotifyFunction(updatePayload types.AppUpdatePayload) {

internal/types/api.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ type SyncListResponse struct {
253253
Entries []*SyncEntry `json:"entries"`
254254
}
255255

256+
type ConfigResponse struct {
257+
DynamicConfig DynamicConfig `json:"dynamic_config"`
258+
}
259+
256260
type AppReloadOption string
257261

258262
const (

tests/commander/test_config.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
config:
2+
env:
3+
CL_CONFIG_FILE: openrun.toml
4+
GOCOVERDIR: ${GOCOVERDIR}/../client
5+
PATH: ${PATH}
6+
tests:
7+
config0001:
8+
command: ../openrun server show-config > config1.json
9+
config0002:
10+
command: diff -b config/dynamic_config.json config1.json
11+
exit-code: 0 # no diff
12+
config0003:
13+
command: ../openrun server update-config config1.json > config2.json
14+
config0004: # version id gets updated after update
15+
command: diff -b config/dynamic_config.json config1.json
16+
stdout:
17+
line-count: 4
18+
exit-code: 1
19+
config0005: # stale update
20+
command: ../openrun server update-config config1.json
21+
stderr: "version id mismatch"
22+
exit-code: 1

tests/commander/test_load_app.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ tests:
8282
stdout: "Disallow: /"
8383
exit-code: 0
8484
load0070: # Update app code
85-
command: perl -pi -e 's/Disk Usage/DiskTest Usage/g' ./disk_usage/app.star && sleep 4
85+
command: perl -pi -e 's/Disk Usage/DiskTest Usage/g' ./disk_usage/app.star && sleep 5
8686
load0080: # with --dev, changes are picked up immediately
8787
command: curl -su "admin:qwerty" localhost:25222/disk_usage_dev
8888
stderr:

0 commit comments

Comments
 (0)