Complete CRUD: add GET-single and DELETE endpoints
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful

Add GET /{name} and DELETE /{name} for zones, address-groups, portgroups,
fabrics, devices, and bindings, plus GET /rules/{id}, with the matching store
Get/Delete methods (deletes bump the generation and 404 on no-match). This gives
the resources full read/update/delete lifecycle so the Terraform provider can
manage them.
This commit is contained in:
benvin
2026-07-19 22:18:07 +10:00
parent 3afaab7d6c
commit 82c7d9c5f7
2 changed files with 209 additions and 1 deletions
+105
View File
@@ -21,33 +21,72 @@ func (s *Server) mountResources(r chi.Router) {
r.Route("/fabrics", func(r chi.Router) {
r.Get("/", s.listFabrics)
r.Get("/{name}", s.getFabric)
r.Put("/{name}", s.putFabric)
r.Delete("/{name}", s.deleteFabric)
})
r.Route("/zones", func(r chi.Router) {
r.Get("/", s.listZones)
r.Get("/{name}", s.getZone)
r.Put("/{name}", s.putZone)
r.Delete("/{name}", s.deleteZone)
})
r.Route("/address-groups", func(r chi.Router) {
r.Get("/", s.listAddressGroups)
r.Get("/{name}", s.getAddressGroup)
r.Put("/{name}", s.putAddressGroup)
r.Delete("/{name}", s.deleteAddressGroup)
})
r.Route("/devices", func(r chi.Router) {
r.Get("/", s.listDevices)
r.Get("/{name}", s.getDevice)
r.Put("/{name}", s.putDevice)
r.Delete("/{name}", s.deleteDevice)
r.Get("/{name}/bindings", s.listBindings)
r.Get("/{name}/bindings/{zone}", s.getBinding)
r.Put("/{name}/bindings/{zone}", s.putBinding)
r.Delete("/{name}/bindings/{zone}", s.deleteBinding)
})
r.Route("/portgroups", func(r chi.Router) {
r.Get("/", s.listPortGroups)
r.Get("/{name}", s.getPortGroup)
r.Put("/{name}", s.putPortGroup)
r.Delete("/{name}", s.deletePortGroup)
})
r.Route("/rules", func(r chi.Router) {
r.Get("/", s.listRules)
r.Post("/", s.createRule)
r.Get("/{id}", s.getRule)
r.Delete("/{id}", s.deleteRule)
})
}
// respondOne writes a single resource, mapping ErrNotFound to 404.
func respondOne(w http.ResponseWriter, v any, err error) {
if err != nil {
if errors.Is(err, store.ErrNotFound) {
writeError(w, http.StatusNotFound, "not found")
return
}
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, v)
}
// respondDelete maps a delete result to 204/404/500.
func respondDelete(w http.ResponseWriter, err error) {
if err != nil {
if errors.Is(err, store.ErrNotFound) {
writeError(w, http.StatusNotFound, "not found")
return
}
writeError(w, http.StatusInternalServerError, err.Error())
return
}
w.WriteHeader(http.StatusNoContent)
}
func (s *Server) listPortGroups(w http.ResponseWriter, r *http.Request) {
list, err := s.store.ListPortGroups(r.Context())
respondList(w, list, err)
@@ -221,6 +260,72 @@ func (s *Server) deleteRule(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
// ---- Get-single and Delete handlers ----------------------------------------
func (s *Server) getFabric(w http.ResponseWriter, r *http.Request) {
f, err := s.store.GetFabric(r.Context(), chi.URLParam(r, "name"))
respondOne(w, f, err)
}
func (s *Server) deleteFabric(w http.ResponseWriter, r *http.Request) {
respondDelete(w, s.store.DeleteFabric(r.Context(), chi.URLParam(r, "name")))
}
func (s *Server) getZone(w http.ResponseWriter, r *http.Request) {
z, err := s.store.GetZone(r.Context(), chi.URLParam(r, "name"))
respondOne(w, z, err)
}
func (s *Server) deleteZone(w http.ResponseWriter, r *http.Request) {
respondDelete(w, s.store.DeleteZone(r.Context(), chi.URLParam(r, "name")))
}
func (s *Server) getAddressGroup(w http.ResponseWriter, r *http.Request) {
g, err := s.store.GetAddressGroup(r.Context(), chi.URLParam(r, "name"))
respondOne(w, g, err)
}
func (s *Server) deleteAddressGroup(w http.ResponseWriter, r *http.Request) {
respondDelete(w, s.store.DeleteAddressGroup(r.Context(), chi.URLParam(r, "name")))
}
func (s *Server) getPortGroup(w http.ResponseWriter, r *http.Request) {
p, err := s.store.GetPortGroup(r.Context(), chi.URLParam(r, "name"))
respondOne(w, p, err)
}
func (s *Server) deletePortGroup(w http.ResponseWriter, r *http.Request) {
respondDelete(w, s.store.DeletePortGroup(r.Context(), chi.URLParam(r, "name")))
}
func (s *Server) getDevice(w http.ResponseWriter, r *http.Request) {
d, err := s.store.GetDevice(r.Context(), chi.URLParam(r, "name"))
respondOne(w, d, err)
}
func (s *Server) deleteDevice(w http.ResponseWriter, r *http.Request) {
respondDelete(w, s.store.DeleteDevice(r.Context(), chi.URLParam(r, "name")))
}
func (s *Server) getBinding(w http.ResponseWriter, r *http.Request) {
b, err := s.store.GetBinding(r.Context(), chi.URLParam(r, "name"), chi.URLParam(r, "zone"))
respondOne(w, b, err)
}
func (s *Server) deleteBinding(w http.ResponseWriter, r *http.Request) {
respondDelete(w, s.store.DeleteBinding(r.Context(), chi.URLParam(r, "name"), chi.URLParam(r, "zone")))
}
func (s *Server) getRule(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
writeError(w, http.StatusBadRequest, "id must be an integer")
return
}
rule, err := s.store.GetRule(r.Context(), id)
respondOne(w, rule, err)
}
// ---- Agent endpoints -------------------------------------------------------
func (s *Server) handleDeviceConfig(w http.ResponseWriter, r *http.Request) {