From b58008d60fd726365342e303709a50474b92f458 Mon Sep 17 00:00:00 2001 From: Jarrod Baumann Date: Thu, 13 Jun 2024 02:27:56 +0000 Subject: [PATCH 1/3] api support for updating a target's subscriptions --- docs/user_guide/api/targets.md | 41 +++++++++++++++++++++++++++++++++- pkg/app/api.go | 37 ++++++++++++++++++++++++++++++ pkg/app/routes.go | 1 + pkg/app/target.go | 21 ++++++++++++++++- 4 files changed, 98 insertions(+), 2 deletions(-) diff --git a/docs/user_guide/api/targets.md b/docs/user_guide/api/targets.md index 5b0ee23b..a580203d 100644 --- a/docs/user_guide/api/targets.md +++ b/docs/user_guide/api/targets.md @@ -193,4 +193,43 @@ Returns an empty body if successful. "Error Text" ] } - ``` \ No newline at end of file + ``` + +## `PATCH /api/v1/targets/{id}/subscriptions` + +Updates existing subscriptions for the target ID + +Returns an empty body if successful. + +=== "Request" + ```bash + curl --request PATCH gnmic-api-address:port/api/v1/targets/192.168.1.131:57400/subscriptions -d '{"subscriptions": ["sub1", "sub2"]}' + ``` +=== "200 OK" + ```json + ``` +=== "404 Not found" + ```json + { + "errors": [ + "target $target not found" + ] + } + ``` +=== "400 Bad Request" + ```json + { + "errors": [ + "subscriptions not found", + "subscription $subscription does not exist" + ] + } + ``` +=== "500 Internal Server Error" + ```json + { + "errors": [ + "Error Text" + ] + } + ``` diff --git a/pkg/app/api.go b/pkg/app/api.go index 49c77c3c..3a2ca7e0 100644 --- a/pkg/app/api.go +++ b/pkg/app/api.go @@ -113,6 +113,43 @@ func (a *App) handleConfigTargetsPost(w http.ResponseWriter, r *http.Request) { a.AddTargetConfig(tc) } +func (a *App) handleConfigTargetsSubscriptions(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id := vars["id"] + if !a.targetConfigExists(id) { + w.WriteHeader(http.StatusNotFound) + json.NewEncoder(w).Encode(APIErrors{Errors: []string{fmt.Sprintf("target %q not found", id)}}) + return + } + body, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(APIErrors{Errors: []string{err.Error()}}) + return + } + defer r.Body.Close() + + var data map[string][]string + err = json.Unmarshal(body, &data) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(APIErrors{Errors: []string{err.Error()}}) + return + } + subs, ok := data["subscriptions"] + if !ok { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(APIErrors{Errors: []string{"subscriptions not found"}}) + return + } + err = a.UpdateTargetSubscription(a.ctx, id, subs) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(APIErrors{Errors: []string{err.Error()}}) + return + } +} + func (a *App) handleConfigTargetsDelete(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] diff --git a/pkg/app/routes.go b/pkg/app/routes.go index 77128be0..52d4a2cc 100644 --- a/pkg/app/routes.go +++ b/pkg/app/routes.go @@ -36,6 +36,7 @@ func (a *App) configRoutes(r *mux.Router) { r.HandleFunc("/config/targets/{id}", a.handleConfigTargetsGet).Methods(http.MethodGet) r.HandleFunc("/config/targets", a.handleConfigTargetsPost).Methods(http.MethodPost) r.HandleFunc("/config/targets/{id}", a.handleConfigTargetsDelete).Methods(http.MethodDelete) + r.HandleFunc("/config/targets/{id}/subscriptions", a.handleConfigTargetsSubscriptions).Methods(http.MethodPatch) // config/subscriptions r.HandleFunc("/config/subscriptions", a.handleConfigSubscriptions).Methods(http.MethodGet) // config/outputs diff --git a/pkg/app/target.go b/pkg/app/target.go index 904c2cd9..f5f2d32b 100644 --- a/pkg/app/target.go +++ b/pkg/app/target.go @@ -43,7 +43,6 @@ func (a *App) initTarget(tc *types.TargetConfig) (*target.Target, error) { return t, nil } return t, nil - } func (a *App) stopTarget(ctx context.Context, name string) error { @@ -96,6 +95,26 @@ func (a *App) DeleteTarget(ctx context.Context, name string) error { return nil } +// UpdateTargetConfig updates the subscriptions for an existing target +func (a *App) UpdateTargetSubscription(ctx context.Context, name string, subs []string) error { + a.configLock.Lock() + for _, subName := range subs { + if _, ok := a.Config.Subscriptions[subName]; !ok { + return fmt.Errorf("subscription %q does not exist", subName) + } + } + targetConfig := a.Config.Targets[name] + targetConfig.Subscriptions = subs + a.configLock.Unlock() + + if err := a.stopTarget(ctx, name); err != nil { + return err + } + + go a.TargetSubscribeStream(ctx, targetConfig) + return nil +} + // AddTargetConfig adds a *TargetConfig to the configuration map func (a *App) AddTargetConfig(tc *types.TargetConfig) { a.Logger.Printf("adding target %s", tc) From 6ddbb3e4c82759e5b45a876f8548b8a135d3d3c7 Mon Sep 17 00:00:00 2001 From: Jarrod Baumann Date: Fri, 14 Jun 2024 22:25:40 +0000 Subject: [PATCH 2/3] maybe we should allow bad requests to unlock the config --- pkg/app/target.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/app/target.go b/pkg/app/target.go index f5f2d32b..69b02a55 100644 --- a/pkg/app/target.go +++ b/pkg/app/target.go @@ -100,6 +100,7 @@ func (a *App) UpdateTargetSubscription(ctx context.Context, name string, subs [] a.configLock.Lock() for _, subName := range subs { if _, ok := a.Config.Subscriptions[subName]; !ok { + a.configLock.Unlock() return fmt.Errorf("subscription %q does not exist", subName) } } From b1042760459e8b7e2c277117101600fb234ed66f Mon Sep 17 00:00:00 2001 From: Jarrod Baumann Date: Thu, 20 Jun 2024 19:43:11 +0000 Subject: [PATCH 3/3] update user_guide with a valid example --- docs/user_guide/api/targets.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/user_guide/api/targets.md b/docs/user_guide/api/targets.md index a580203d..47d5c687 100644 --- a/docs/user_guide/api/targets.md +++ b/docs/user_guide/api/targets.md @@ -220,7 +220,6 @@ Returns an empty body if successful. ```json { "errors": [ - "subscriptions not found", "subscription $subscription does not exist" ] }