Skip to content

Commit

Permalink
feat(api): add support for validating JSON schema on PUT
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgeMac committed Jul 31, 2023
1 parent 166ba29 commit 2237a48
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 6 deletions.
26 changes: 25 additions & 1 deletion cmd/cup/ctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"go.flipt.io/cup/pkg/api/core"
"go.flipt.io/cup/pkg/encoding"
"golang.org/x/exp/slog"
)

func definitions(cfg config, client *http.Client) error {
Expand Down Expand Up @@ -149,7 +150,7 @@ func apply(cfg config, client *http.Client, source string) (err error) {
return err
}

defs, err := getDefintions(cfg, client)
defs, err := getDefintionsByAPIVersionKind(cfg, client)
if err != nil {
return err
}
Expand Down Expand Up @@ -186,6 +187,13 @@ func apply(cfg config, client *http.Client, source string) (err error) {
}()

if resp.StatusCode != http.StatusOK {
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("reading unexpected response body: %w", err)
}

slog.Error("Applying resource", "response", string(body))

return fmt.Errorf("unexpected status: %q", resp.Status)
}

Expand Down Expand Up @@ -260,6 +268,22 @@ func getGVK(cfg config, client *http.Client, typ string) (group, version, kind s
return
}

func getDefintionsByAPIVersionKind(cfg config, client *http.Client) (map[string]*core.ResourceDefinition, error) {
m := map[string]*core.ResourceDefinition{}
defs, err := getDefintions(cfg, client)
if err != nil {
return nil, err
}

for _, def := range defs {
for version := range def.Spec.Versions {
m[path.Join(def.Spec.Group, version, def.Names.Kind)] = def
}
}

return m, nil
}

func getDefintions(cfg config, client *http.Client) (map[string]*core.ResourceDefinition, error) {
req, err := http.NewRequest(http.MethodGet, cfg.Address()+"/apis", nil)
if err != nil {
Expand Down
6 changes: 4 additions & 2 deletions cmd/cupd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,11 @@ func serve(ctx *cli.Context) error {
return fmt.Errorf("controller type not supported: %q", controllerConf.Type)
}

slog.Debug("Registering Controller", "kind", typ)
if err := srv.Register(controller, def); err != nil {
return fmt.Errorf("registering resource %q: %w", typ, err)
}

srv.Register(controller, def)
slog.Debug("Registered Resource Controller", "kind", typ, "controller", binding.Controller)
}

slog.Info("Listening...", "address", cfg.API.Address)
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ require (
github.com/sergi/go-diff v1.1.0 // indirect
github.com/skeema/knownhosts v1.1.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/mod v0.11.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIn
github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
Expand All @@ -85,6 +86,12 @@ github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
25 changes: 22 additions & 3 deletions pkg/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/cors"
"github.com/oklog/ulid/v2"
"github.com/xeipuuv/gojsonschema"
"go.flipt.io/cup/pkg/api/core"
"go.flipt.io/cup/pkg/containers"
"go.flipt.io/cup/pkg/controllers"
Expand Down Expand Up @@ -101,11 +102,11 @@ func (s *Server) addDefinition(def *core.ResourceDefinition, version string) {

// Register adds a new controller and definition with a particular filesystem to the server.
// This may happen dynamically in the future, so it is guarded with a write lock.
func (s *Server) Register(cntl Controller, def *core.ResourceDefinition) {
func (s *Server) Register(cntl Controller, def *core.ResourceDefinition) error {
s.mu.Lock()
defer s.mu.Unlock()

for version := range def.Spec.Versions {
for version, schema := range def.Spec.Versions {
var (
version = version
prefix = fmt.Sprintf("/apis/%s/%s/namespaces/{ns}/%s", def.Spec.Group, version, def.Names.Plural)
Expand All @@ -115,6 +116,11 @@ func (s *Server) Register(cntl Controller, def *core.ResourceDefinition) {
// update sources map
s.addDefinition(def, version)

schema, err := gojsonschema.NewSchema(gojsonschema.NewBytesLoader(schema))
if err != nil {
return err
}

// list kind
s.mux.Get(prefix, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := s.fs.View(r.Context(), s.rev, func(f fs.FS) error {
Expand Down Expand Up @@ -173,7 +179,18 @@ func (s *Server) Register(cntl Controller, def *core.ResourceDefinition) {
s.mux.Put(named, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var resource core.Resource
if err := json.NewDecoder(r.Body).Decode(&resource); err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

res, err := schema.Validate(gojsonschema.NewBytesLoader(resource.Spec))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if !res.Valid() {
http.Error(w, fmt.Sprintf("%v", res.Errors()), http.StatusBadRequest)
return
}

Expand Down Expand Up @@ -242,6 +259,8 @@ func (s *Server) Register(cntl Controller, def *core.ResourceDefinition) {
}
}))
}

return nil
}

func (s *Server) handleSourceDefinitions(w http.ResponseWriter, r *http.Request) {
Expand Down

0 comments on commit 2237a48

Please sign in to comment.