diff --git a/main.go b/main.go index 4790345..1dc94a7 100644 --- a/main.go +++ b/main.go @@ -35,6 +35,9 @@ func main() { func run(_ *cli.Context) error { ctx := signals.SetupSignalHandler(context.Background()) debugconfig.MustSetupDebug() - s := config.MustServer(ctx) + s, err := config.ToServer(ctx) + if err != nil { + return err + } return s.ListenAndServe(ctx, config.HTTPSListenPort, config.HTTPListenPort, nil) } diff --git a/pkg/accesscontrol/access_control.go b/pkg/accesscontrol/access_control.go index 7c19fbf..cc1b34e 100644 --- a/pkg/accesscontrol/access_control.go +++ b/pkg/accesscontrol/access_control.go @@ -1,10 +1,9 @@ package accesscontrol import ( - "fmt" - - "github.com/rancher/steve/pkg/schemaserver/server" - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/server" + "github.com/rancher/apiserver/pkg/types" + "github.com/rancher/steve/pkg/attributes" ) type AccessControl struct { @@ -16,9 +15,11 @@ func NewAccessControl() *AccessControl { } func (a *AccessControl) CanWatch(apiOp *types.APIRequest, schema *types.APISchema) error { - access := GetAccessListMap(schema) - if _, ok := access["watch"]; ok { - return nil + if attributes.GVK(schema).Kind != "" { + access := GetAccessListMap(schema) + if _, ok := access["watch"]; ok { + return nil + } } - return fmt.Errorf("watch not allowed") + return a.SchemaBasedAccess.CanWatch(apiOp, schema) } diff --git a/pkg/accesscontrol/access_set.go b/pkg/accesscontrol/access_set.go index b221ea1..7597cf7 100644 --- a/pkg/accesscontrol/access_set.go +++ b/pkg/accesscontrol/access_set.go @@ -3,8 +3,8 @@ package accesscontrol import ( "sort" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/types" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" ) diff --git a/pkg/attributes/attributes.go b/pkg/attributes/attributes.go index f97550e..f07034b 100644 --- a/pkg/attributes/attributes.go +++ b/pkg/attributes/attributes.go @@ -3,7 +3,7 @@ package attributes import ( "fmt" - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/wrangler/pkg/data/convert" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/client/factory.go b/pkg/client/factory.go index db8159e..f9119c4 100644 --- a/pkg/client/factory.go +++ b/pkg/client/factory.go @@ -5,8 +5,8 @@ import ( "net/http" "time" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/types" "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" diff --git a/pkg/clustercache/controller.go b/pkg/clustercache/controller.go index 0035b1f..48fc6dc 100644 --- a/pkg/clustercache/controller.go +++ b/pkg/clustercache/controller.go @@ -5,9 +5,9 @@ import ( "sync" "time" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" "github.com/rancher/steve/pkg/schema" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/merr" "github.com/rancher/wrangler/pkg/summary/client" "github.com/rancher/wrangler/pkg/summary/informer" diff --git a/pkg/controllers/schema/schemas.go b/pkg/controllers/schema/schemas.go index 14bffd7..9364754 100644 --- a/pkg/controllers/schema/schemas.go +++ b/pkg/controllers/schema/schemas.go @@ -6,11 +6,11 @@ import ( "sync/atomic" "time" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" + "github.com/rancher/steve/pkg/resources/common" schema2 "github.com/rancher/steve/pkg/schema" "github.com/rancher/steve/pkg/schema/converter" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/resources/common" apiextcontrollerv1beta1 "github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io/v1beta1" v1 "github.com/rancher/wrangler-api/pkg/generated/controllers/apiregistration.k8s.io/v1" "github.com/sirupsen/logrus" diff --git a/pkg/dashboard/ui.go b/pkg/dashboard/ui.go index 1d0c262..51d4c21 100644 --- a/pkg/dashboard/ui.go +++ b/pkg/dashboard/ui.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/gorilla/mux" - "github.com/rancher/steve/pkg/responsewriter" - "github.com/rancher/steve/pkg/schemaserver/parse" + "github.com/rancher/apiserver/pkg/middleware" + "github.com/rancher/apiserver/pkg/parse" "github.com/sirupsen/logrus" ) @@ -28,15 +28,17 @@ func content(uiSetting func() string) http.Handler { } func Route(next http.Handler, uiSetting func() string) http.Handler { - uiContent := responsewriter.NewMiddlewareChain(responsewriter.Gzip, - responsewriter.DenyFrameOptions, - responsewriter.CacheMiddleware("json", "js", "css")).Handler(content(uiSetting)) + uiContent := middleware.NewMiddlewareChain(middleware.Gzip, + middleware.DenyFrameOptions, + middleware.CacheMiddleware("json", "js", "css")).Handler(content(uiSetting)) root := mux.NewRouter() root.UseEncodedPath() + root.Path("/").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + http.Redirect(rw, req, "/dashboard/", http.StatusFound) + }) root.Path("/dashboard").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - rw.Header().Add("Location", "/dashboard/") - rw.WriteHeader(http.StatusFound) + http.Redirect(rw, req, "/dashboard/", http.StatusFound) }) root.PathPrefix("/dashboard/assets").Handler(uiContent) root.PathPrefix("/dashboard/translations").Handler(uiContent) diff --git a/pkg/server/resources/apigroups/apigroup.go b/pkg/resources/apigroups/apigroup.go similarity index 93% rename from pkg/server/resources/apigroups/apigroup.go rename to pkg/resources/apigroups/apigroup.go index 5d0cc19..5bc0082 100644 --- a/pkg/server/resources/apigroups/apigroup.go +++ b/pkg/resources/apigroups/apigroup.go @@ -5,8 +5,8 @@ import ( "github.com/rancher/steve/pkg/schema" - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/store/empty" + "github.com/rancher/apiserver/pkg/types" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" ) diff --git a/pkg/server/resources/clusters/clusters.go b/pkg/resources/clusters/clusters.go similarity index 93% rename from pkg/server/resources/clusters/clusters.go rename to pkg/resources/clusters/clusters.go index e2a0135..6d41b3d 100644 --- a/pkg/server/resources/clusters/clusters.go +++ b/pkg/resources/clusters/clusters.go @@ -5,12 +5,12 @@ import ( "net/http" "time" + "github.com/rancher/apiserver/pkg/store/empty" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/clustercache" - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/proxy" - "github.com/rancher/steve/pkg/server/store/switchschema" - "github.com/rancher/steve/pkg/server/store/switchstore" + "github.com/rancher/steve/pkg/stores/proxy" + "github.com/rancher/steve/pkg/stores/switchschema" + "github.com/rancher/steve/pkg/stores/switchstore" "github.com/rancher/wrangler/pkg/data" "github.com/rancher/wrangler/pkg/schemas/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/server/resources/clusters/shell.go b/pkg/resources/clusters/shell.go similarity index 99% rename from pkg/server/resources/clusters/shell.go rename to pkg/resources/clusters/shell.go index b54b0f5..82a6d25 100644 --- a/pkg/server/resources/clusters/shell.go +++ b/pkg/resources/clusters/shell.go @@ -7,7 +7,7 @@ import ( "net/http/httputil" "time" - "github.com/rancher/steve/pkg/server/store/proxy" + "github.com/rancher/steve/pkg/stores/proxy" "github.com/rancher/wrangler/pkg/condition" "github.com/rancher/wrangler/pkg/schemas/validation" "github.com/sirupsen/logrus" diff --git a/pkg/server/resources/common/dynamiccolumns.go b/pkg/resources/common/dynamiccolumns.go similarity index 97% rename from pkg/server/resources/common/dynamiccolumns.go rename to pkg/resources/common/dynamiccolumns.go index 2ca1acd..42c507f 100644 --- a/pkg/server/resources/common/dynamiccolumns.go +++ b/pkg/resources/common/dynamiccolumns.go @@ -4,8 +4,8 @@ import ( "context" "fmt" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/ratelimit" "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/server/resources/common/formatter.go b/pkg/resources/common/formatter.go similarity index 93% rename from pkg/server/resources/common/formatter.go rename to pkg/resources/common/formatter.go index b5564c2..d702284 100644 --- a/pkg/server/resources/common/formatter.go +++ b/pkg/resources/common/formatter.go @@ -3,10 +3,10 @@ package common import ( "strings" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/schema" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/proxy" + "github.com/rancher/steve/pkg/stores/proxy" "github.com/rancher/wrangler/pkg/data" "github.com/rancher/wrangler/pkg/summary" "k8s.io/apimachinery/pkg/api/meta" diff --git a/pkg/server/resources/counts/counts.go b/pkg/resources/counts/counts.go similarity index 98% rename from pkg/server/resources/counts/counts.go rename to pkg/resources/counts/counts.go index 50e274e..c57eb48 100644 --- a/pkg/server/resources/counts/counts.go +++ b/pkg/resources/counts/counts.go @@ -5,11 +5,11 @@ import ( "strconv" "sync" + "github.com/rancher/apiserver/pkg/store/empty" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/attributes" "github.com/rancher/steve/pkg/clustercache" - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/summary" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/server/resources/helm/formatter.go b/pkg/resources/helm/formatter.go similarity index 96% rename from pkg/server/resources/helm/formatter.go rename to pkg/resources/helm/formatter.go index 29284fc..3d324f3 100644 --- a/pkg/server/resources/helm/formatter.go +++ b/pkg/resources/helm/formatter.go @@ -1,9 +1,9 @@ package helm import ( + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" "github.com/rancher/steve/pkg/schema/converter" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/server/resources/helm/helm2.go b/pkg/resources/helm/helm2.go similarity index 100% rename from pkg/server/resources/helm/helm2.go rename to pkg/resources/helm/helm2.go diff --git a/pkg/server/resources/helm/helm3.go b/pkg/resources/helm/helm3.go similarity index 100% rename from pkg/server/resources/helm/helm3.go rename to pkg/resources/helm/helm3.go diff --git a/pkg/server/resources/helm/register.go b/pkg/resources/helm/register.go similarity index 80% rename from pkg/server/resources/helm/register.go rename to pkg/resources/helm/register.go index b3af89b..87c8d15 100644 --- a/pkg/server/resources/helm/register.go +++ b/pkg/resources/helm/register.go @@ -3,8 +3,8 @@ package helm import ( "net/http" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/partition" + "github.com/rancher/apiserver/pkg/types" + "github.com/rancher/steve/pkg/stores/partition" ) func Register(schemas *types.APISchemas) { diff --git a/pkg/server/resources/helm/release.go b/pkg/resources/helm/release.go similarity index 100% rename from pkg/server/resources/helm/release.go rename to pkg/resources/helm/release.go diff --git a/pkg/server/resources/helm/store.go b/pkg/resources/helm/store.go similarity index 89% rename from pkg/server/resources/helm/store.go rename to pkg/resources/helm/store.go index 9b0d08d..04a8535 100644 --- a/pkg/server/resources/helm/store.go +++ b/pkg/resources/helm/store.go @@ -3,11 +3,11 @@ package helm import ( "strings" - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/partition" - "github.com/rancher/steve/pkg/server/store/selector" - "github.com/rancher/steve/pkg/server/store/switchschema" + "github.com/rancher/apiserver/pkg/store/empty" + "github.com/rancher/apiserver/pkg/types" + "github.com/rancher/steve/pkg/stores/partition" + "github.com/rancher/steve/pkg/stores/selector" + "github.com/rancher/steve/pkg/stores/switchschema" "github.com/rancher/wrangler/pkg/schemas/validation" "k8s.io/apimachinery/pkg/labels" ) diff --git a/pkg/server/resources/helm/types.go b/pkg/resources/helm/types.go similarity index 100% rename from pkg/server/resources/helm/types.go rename to pkg/resources/helm/types.go diff --git a/pkg/server/resources/schema.go b/pkg/resources/schema.go similarity index 62% rename from pkg/server/resources/schema.go rename to pkg/resources/schema.go index d7f3738..355c000 100644 --- a/pkg/server/resources/schema.go +++ b/pkg/resources/schema.go @@ -3,27 +3,27 @@ package resources import ( "context" + "github.com/rancher/apiserver/pkg/store/apiroot" + "github.com/rancher/apiserver/pkg/subscribe" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/client" "github.com/rancher/steve/pkg/clustercache" + "github.com/rancher/steve/pkg/resources/apigroups" + "github.com/rancher/steve/pkg/resources/clusters" + "github.com/rancher/steve/pkg/resources/common" + "github.com/rancher/steve/pkg/resources/counts" + "github.com/rancher/steve/pkg/resources/helm" + "github.com/rancher/steve/pkg/resources/userpreferences" "github.com/rancher/steve/pkg/schema" - "github.com/rancher/steve/pkg/schemaserver/store/apiroot" - "github.com/rancher/steve/pkg/schemaserver/subscribe" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/resources/apigroups" - "github.com/rancher/steve/pkg/server/resources/clusters" - "github.com/rancher/steve/pkg/server/resources/common" - "github.com/rancher/steve/pkg/server/resources/counts" - "github.com/rancher/steve/pkg/server/resources/helm" - "github.com/rancher/steve/pkg/server/resources/userpreferences" - "github.com/rancher/steve/pkg/server/store/proxy" + "github.com/rancher/steve/pkg/stores/proxy" "k8s.io/client-go/discovery" ) func DefaultSchemas(ctx context.Context, baseSchema *types.APISchemas, ccache clustercache.ClusterCache, cg proxy.ClientGetter) (*types.APISchemas, error) { counts.Register(baseSchema, ccache) subscribe.Register(baseSchema) - apiroot.Register(baseSchema, []string{"v1"}, []string{"proxy:/apis"}) + apiroot.Register(baseSchema, []string{"v1"}, "proxy:/apis") userpreferences.Register(baseSchema, cg) helm.Register(baseSchema) diff --git a/pkg/server/resources/schemas/template.go b/pkg/resources/schemas/template.go similarity index 95% rename from pkg/server/resources/schemas/template.go rename to pkg/resources/schemas/template.go index 7515eea..b32b2c5 100644 --- a/pkg/server/resources/schemas/template.go +++ b/pkg/resources/schemas/template.go @@ -5,12 +5,12 @@ import ( "sync" "time" - "github.com/rancher/steve/pkg/schemaserver/builtin" + "github.com/rancher/apiserver/pkg/builtin" + schemastore "github.com/rancher/apiserver/pkg/store/schema" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/schema" - schemastore "github.com/rancher/steve/pkg/schemaserver/store/schema" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/broadcast" "github.com/rancher/wrangler/pkg/schemas/validation" "github.com/sirupsen/logrus" diff --git a/pkg/server/resources/userpreferences/configmap.go b/pkg/resources/userpreferences/configmap.go similarity index 94% rename from pkg/server/resources/userpreferences/configmap.go rename to pkg/resources/userpreferences/configmap.go index e19865b..f9b4366 100644 --- a/pkg/server/resources/userpreferences/configmap.go +++ b/pkg/resources/userpreferences/configmap.go @@ -1,10 +1,10 @@ package userpreferences import ( + "github.com/rancher/apiserver/pkg/store/empty" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/norman/types/convert" - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/proxy" + "github.com/rancher/steve/pkg/stores/proxy" "github.com/rancher/wrangler/pkg/schemas/validation" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" diff --git a/pkg/server/resources/userpreferences/rancherpref.go b/pkg/resources/userpreferences/rancherpref.go similarity index 96% rename from pkg/server/resources/userpreferences/rancherpref.go rename to pkg/resources/userpreferences/rancherpref.go index 14fd1a8..d809c84 100644 --- a/pkg/server/resources/userpreferences/rancherpref.go +++ b/pkg/resources/userpreferences/rancherpref.go @@ -1,10 +1,10 @@ package userpreferences import ( + "github.com/rancher/apiserver/pkg/store/empty" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/proxy" + "github.com/rancher/steve/pkg/stores/proxy" "github.com/rancher/wrangler/pkg/data/convert" "github.com/rancher/wrangler/pkg/schemas/validation" apierrors "k8s.io/apimachinery/pkg/api/errors" diff --git a/pkg/server/resources/userpreferences/userpreferences.go b/pkg/resources/userpreferences/userpreferences.go similarity index 93% rename from pkg/server/resources/userpreferences/userpreferences.go rename to pkg/resources/userpreferences/userpreferences.go index a4c267a..716b476 100644 --- a/pkg/server/resources/userpreferences/userpreferences.go +++ b/pkg/resources/userpreferences/userpreferences.go @@ -3,9 +3,9 @@ package userpreferences import ( "net/http" - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/proxy" + "github.com/rancher/apiserver/pkg/store/empty" + "github.com/rancher/apiserver/pkg/types" + "github.com/rancher/steve/pkg/stores/proxy" "github.com/rancher/wrangler/pkg/name" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/endpoints/request" diff --git a/pkg/responsewriter/cache.go b/pkg/responsewriter/cache.go deleted file mode 100644 index e5ece3c..0000000 --- a/pkg/responsewriter/cache.go +++ /dev/null @@ -1,49 +0,0 @@ -package responsewriter - -import ( - "net/http" - "strings" - - "github.com/gorilla/mux" -) - -func CacheMiddleware(suffixes ...string) mux.MiddlewareFunc { - return mux.MiddlewareFunc(func(handler http.Handler) http.Handler { - return Cache(handler, suffixes...) - }) -} - -func Cache(handler http.Handler, suffixes ...string) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - i := strings.LastIndex(r.URL.Path, ".") - if i >= 0 { - for _, suffix := range suffixes { - if suffix == r.URL.Path[i+1:] { - w.Header().Set("Cache-Control", "max-age=31536000, public") - } - } - } - handler.ServeHTTP(w, r) - }) -} - -func NoCache(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") - handler.ServeHTTP(w, r) - }) -} - -func DenyFrameOptions(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("X-Frame-Options", "deny") - handler.ServeHTTP(w, r) - }) -} - -func ContentTypeOptions(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("X-Content-Type-Options", "nosniff") - handler.ServeHTTP(w, r) - }) -} diff --git a/pkg/responsewriter/content.go b/pkg/responsewriter/content.go deleted file mode 100644 index 101d4f8..0000000 --- a/pkg/responsewriter/content.go +++ /dev/null @@ -1,42 +0,0 @@ -package responsewriter - -import ( - "bufio" - "fmt" - "net" - "net/http" - "reflect" - "strings" -) - -type ContentTypeWriter struct { - http.ResponseWriter -} - -func (c ContentTypeWriter) Write(b []byte) (int, error) { - found := false - for k := range c.Header() { - if strings.EqualFold(k, "Content-Type") { - found = true - break - } - } - if !found { - c.Header().Set("Content-Type", http.DetectContentType(b)) - } - return c.ResponseWriter.Write(b) -} - -func ContentType(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - writer := ContentTypeWriter{ResponseWriter: w} - handler.ServeHTTP(writer, r) - }) -} - -func (c ContentTypeWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - if hijacker, ok := c.ResponseWriter.(http.Hijacker); ok { - return hijacker.Hijack() - } - return nil, nil, fmt.Errorf("Upstream ResponseWriter of type %v does not implement http.Hijacker", reflect.TypeOf(c.ResponseWriter)) -} diff --git a/pkg/responsewriter/gzip.go b/pkg/responsewriter/gzip.go deleted file mode 100644 index 31ca8c1..0000000 --- a/pkg/responsewriter/gzip.go +++ /dev/null @@ -1,70 +0,0 @@ -package responsewriter - -import ( - "bufio" - "compress/gzip" - "fmt" - "io" - "net" - "net/http" - "reflect" - "strings" -) - -type wrapWriter struct { - gzipResponseWriter - - code int -} - -type gzipResponseWriter struct { - io.Writer - http.ResponseWriter -} - -func (g gzipResponseWriter) Write(b []byte) (int, error) { - // Header logic is kept here in case the user does not use WriteHeader - g.Header().Set("Content-Encoding", "gzip") - g.Header().Del("Content-Length") - - return g.Writer.Write(b) -} - -// Close uses gzip to write gzip footer if message is gzip encoded -func (g gzipResponseWriter) Close(writer *gzip.Writer) { - if g.Header().Get("Content-Encoding") == "gzip" { - writer.Close() - } -} - -// WriteHeader sets gzip encoding and removes length. Should always be used when using gzip writer. -func (g gzipResponseWriter) WriteHeader(statusCode int) { - g.Header().Set("Content-Encoding", "gzip") - g.Header().Del("Content-Length") - g.ResponseWriter.WriteHeader(statusCode) -} - -// Gzip creates a gzip writer if gzip encoding is accepted. -func Gzip(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { - handler.ServeHTTP(w, r) - return - } - gz := gzip.NewWriter(w) - - gzw := &wrapWriter{gzipResponseWriter{Writer: gz, ResponseWriter: w}, http.StatusOK} - defer gzw.Close(gz) - - // Content encoding will be set once Write or WriteHeader is called, to avoid gzipping empty messages - handler.ServeHTTP(gzw, r) - }) -} - -// Hijack must be implemented to properly chain with handlers expecting a hijacker handler to be passed -func (g *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - if hijacker, ok := g.ResponseWriter.(http.Hijacker); ok { - return hijacker.Hijack() - } - return nil, nil, fmt.Errorf("Upstream ResponseWriter of type %v does not implement http.Hijacker", reflect.TypeOf(g.ResponseWriter)) -} diff --git a/pkg/responsewriter/gzip_test.go b/pkg/responsewriter/gzip_test.go deleted file mode 100644 index 3e13f7c..0000000 --- a/pkg/responsewriter/gzip_test.go +++ /dev/null @@ -1,174 +0,0 @@ -package responsewriter - -import ( - "compress/gzip" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" -) - -// All other writers will attempt additional unnecessary logic -// Implements http.responseWriter and io.Writer -type DummyWriter struct { - header map[string][]string - buffer []byte -} - -type DummyHandler struct { -} - -type DummyHandlerWithWrite struct { - DummyHandler - next http.Handler -} - -func NewDummyWriter() *DummyWriter { - return &DummyWriter{map[string][]string{}, []byte{}} -} - -func NewRequest(accept string) *http.Request { - return &http.Request{ - Header: map[string][]string{"Accept-Encoding": {accept}}, - } -} - -func (d *DummyWriter) Header() http.Header { - return d.header -} - -func (d *DummyWriter) Write(p []byte) (n int, err error) { - d.buffer = append(d.buffer, p...) - return 0, nil -} - -func (d *DummyWriter) WriteHeader(int) { -} - -func (d *DummyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { -} - -func (d *DummyHandlerWithWrite) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Write([]byte{0, 0}) - if d.next != nil { - d.next.ServeHTTP(w, r) - } -} - -// TestWriteHeader asserts content-length header is deleted and content-encoding header is set to gzip -func TestWriteHeader(t *testing.T) { - assert := assert.New(t) - - w := NewDummyWriter() - gz := &gzipResponseWriter{gzip.NewWriter(w), w} - - gz.Header().Set("Content-Length", "80") - gz.WriteHeader(400) - // Content-Length should have been deleted in WriterHeader, resulting in empty string - assert.Equal("", gz.Header().Get("Content-Length")) - assert.Equal(1, len(w.header["Content-Encoding"])) - assert.Equal("gzip", gz.Header().Get("Content-Encoding")) -} - -// TestSetContentWithoutWrite asserts content-encoding is NOT "gzip" if accept-encoding header does not contain gzip -func TestSetContentWithoutWrite(t *testing.T) { - assert := assert.New(t) - - // Test content encoding header when write is not used - handlerFunc := Gzip(&DummyHandler{}) - - // Test when accept-encoding only contains gzip - rw := NewDummyWriter() - req := NewRequest("gzip") - handlerFunc.ServeHTTP(rw, req) - // Content encoding should be empty since write has not been used - assert.Equal(0, len(rw.header["Content-Encoding"])) - assert.Equal("", rw.Header().Get("Content-Encoding")) - - // Test when accept-encoding contains multiple options, including gzip - rw = NewDummyWriter() - req = NewRequest("json, xml, gzip") - handlerFunc.ServeHTTP(rw, req) - assert.Equal(0, len(rw.header["Content-Encoding"])) - assert.Equal("", rw.Header().Get("Content-Encoding")) - - // Test when accept-encoding is empty - req = NewRequest("") - rw = NewDummyWriter() - handlerFunc.ServeHTTP(rw, req) - assert.Equal(0, len(rw.header["Content-Encoding"])) - assert.Equal("", rw.Header().Get("Content-Encoding")) - - // Test when accept-encoding is is not empty but does not include gzip - req = NewRequest("json, xml") - rw = NewDummyWriter() - handlerFunc.ServeHTTP(rw, req) - assert.Equal(0, len(rw.header["Content-Encoding"])) - assert.Equal("", rw.Header().Get("Content-Encoding")) -} - -// TestSetContentWithWrite asserts content-encoding is "gzip" if accept-encoding header contains gzip -func TestSetContentWithWrite(t *testing.T) { - assert := assert.New(t) - - // Test content encoding header when write is used - handlerFunc := Gzip(&DummyHandlerWithWrite{}) - - // Test when accept-encoding only contains gzip - req := NewRequest("gzip") - rw := NewDummyWriter() - handlerFunc.ServeHTTP(rw, req) - // Content encoding should be gzip since write has been used - assert.Equal(1, len(rw.header["Content-Encoding"])) - assert.Equal("gzip", rw.Header().Get("Content-Encoding")) - - // Test when accept-encoding contains multiple options, including gzip - req = NewRequest("json, xml, gzip") - rw = NewDummyWriter() - handlerFunc.ServeHTTP(rw, req) - // Content encoding should be gzip since write has been used - assert.Equal(1, len(rw.header["Content-Encoding"])) - assert.Equal("gzip", rw.Header().Get("Content-Encoding")) - - // Test when accept-encoding is empty - req = NewRequest("") - rw = NewDummyWriter() - handlerFunc.ServeHTTP(rw, req) - // Content encoding should be empty since gzip is not an accepted encoding - assert.Equal(0, len(rw.header["Content-Encoding"])) - assert.Equal("", rw.Header().Get("Content-Encoding")) - - // Test when accept-encoding is is not empty but does not include gzip - req = NewRequest("json, xml") - rw = NewDummyWriter() - handlerFunc.ServeHTTP(rw, req) - // Content encoding should be empty since gzip is not an accepted encoding - assert.Equal(0, len(rw.header["Content-Encoding"])) - assert.Equal("", rw.Header().Get("Content-Encoding")) -} - -// TestMultipleWrites ensures that Write can be used multiple times -func TestMultipleWrites(t *testing.T) { - assert := assert.New(t) - - // Handler function that contains one writing handler - handlerFuncOneWrite := Gzip(&DummyHandlerWithWrite{}) - - // Handler function that contains a chain of two writing handlers - handlerFuncTwoWrites := Gzip(&DummyHandlerWithWrite{next: &DummyHandlerWithWrite{}}) - - req := NewRequest("gzip") - rw := NewDummyWriter() - handlerFuncOneWrite.ServeHTTP(rw, req) - oneWriteResult := rw.buffer - - req = NewRequest("gzip") - rw = NewDummyWriter() - handlerFuncTwoWrites.ServeHTTP(rw, req) - multiWriteResult := rw.buffer - - // Content encoding should be gzip since write has been used (twice) - assert.Equal(1, len(rw.header["Content-Encoding"])) - assert.Equal("gzip", rw.Header().Get("Content-Encoding")) - assert.NotEqual(multiWriteResult, oneWriteResult) -} diff --git a/pkg/responsewriter/middleware.go b/pkg/responsewriter/middleware.go deleted file mode 100644 index f8f2b7e..0000000 --- a/pkg/responsewriter/middleware.go +++ /dev/null @@ -1,24 +0,0 @@ -package responsewriter - -import ( - "net/http" - - "github.com/gorilla/mux" -) - -type MiddlewareChain struct { - middleWares []mux.MiddlewareFunc -} - -func NewMiddlewareChain(middleWares ...mux.MiddlewareFunc) *MiddlewareChain { - return &MiddlewareChain{middleWares: middleWares} -} - -func (m *MiddlewareChain) Handler(handler http.Handler) http.Handler { - rtn := handler - for i := len(m.middleWares) - 1; i >= 0; i-- { - w := m.middleWares[i] - rtn = w.Middleware(rtn) - } - return rtn -} diff --git a/pkg/schema/collection.go b/pkg/schema/collection.go index ce0324e..2427254 100644 --- a/pkg/schema/collection.go +++ b/pkg/schema/collection.go @@ -6,10 +6,10 @@ import ( "strings" "sync" + "github.com/rancher/apiserver/pkg/server" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/server" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/name" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/schema/converter/crd.go b/pkg/schema/converter/crd.go index 492bf21..d0ea92a 100644 --- a/pkg/schema/converter/crd.go +++ b/pkg/schema/converter/crd.go @@ -1,9 +1,9 @@ package converter import ( + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" "github.com/rancher/steve/pkg/schema/table" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io/v1beta1" "github.com/rancher/wrangler/pkg/schemas" beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" diff --git a/pkg/schema/converter/discovery.go b/pkg/schema/converter/discovery.go index c23da18..98bf3bf 100644 --- a/pkg/schema/converter/discovery.go +++ b/pkg/schema/converter/discovery.go @@ -3,8 +3,8 @@ package converter import ( "strings" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/merr" "github.com/rancher/wrangler/pkg/schemas" "github.com/sirupsen/logrus" diff --git a/pkg/schema/converter/k8stonorman.go b/pkg/schema/converter/k8stonorman.go index a1a5f54..6fd3977 100644 --- a/pkg/schema/converter/k8stonorman.go +++ b/pkg/schema/converter/k8stonorman.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io/v1beta1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" diff --git a/pkg/schema/converter/openapi.go b/pkg/schema/converter/openapi.go index 76bd5da..e2b0124 100644 --- a/pkg/schema/converter/openapi.go +++ b/pkg/schema/converter/openapi.go @@ -1,8 +1,8 @@ package converter import ( + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/data/convert" "github.com/rancher/wrangler/pkg/schemas" "github.com/sirupsen/logrus" diff --git a/pkg/schema/converter/openapiv3.go b/pkg/schema/converter/openapiv3.go index d5b15a8..451bd49 100644 --- a/pkg/schema/converter/openapiv3.go +++ b/pkg/schema/converter/openapiv3.go @@ -1,7 +1,7 @@ package converter import ( - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/wrangler/pkg/schemas" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" ) diff --git a/pkg/schema/factory.go b/pkg/schema/factory.go index 7e1263f..24d6f9d 100644 --- a/pkg/schema/factory.go +++ b/pkg/schema/factory.go @@ -5,10 +5,10 @@ import ( "net/http" "time" + "github.com/rancher/apiserver/pkg/builtin" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/builtin" - "github.com/rancher/steve/pkg/schemaserver/types" "k8s.io/apiserver/pkg/authentication/user" ) diff --git a/pkg/schema/table/mapper.go b/pkg/schema/table/mapper.go index b73f822..64d4da3 100644 --- a/pkg/schema/table/mapper.go +++ b/pkg/schema/table/mapper.go @@ -1,8 +1,8 @@ package table import ( + types2 "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" - types2 "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/data" types "github.com/rancher/wrangler/pkg/schemas" "github.com/rancher/wrangler/pkg/schemas/mappers" diff --git a/pkg/schemaserver/builtin/schema.go b/pkg/schemaserver/builtin/schema.go deleted file mode 100644 index 3bb9f22..0000000 --- a/pkg/schemaserver/builtin/schema.go +++ /dev/null @@ -1,88 +0,0 @@ -package builtin - -import ( - "net/http" - - "github.com/rancher/steve/pkg/schemaserver/store/schema" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas" - "github.com/rancher/wrangler/pkg/slice" -) - -var ( - Schema = types.APISchema{ - Schema: &schemas.Schema{ - ID: "schema", - PluralName: "schemas", - CollectionMethods: []string{"GET"}, - ResourceMethods: []string{"GET"}, - ResourceFields: map[string]schemas.Field{ - "collectionActions": {Type: "map[json]"}, - "collectionFields": {Type: "map[json]"}, - "collectionFilters": {Type: "map[json]"}, - "collectionMethods": {Type: "array[string]"}, - "pluralName": {Type: "string"}, - "resourceActions": {Type: "map[json]"}, - "attributes": {Type: "map[json]"}, - "resourceFields": {Type: "map[json]"}, - "resourceMethods": {Type: "array[string]"}, - "version": {Type: "map[json]"}, - }, - }, - Formatter: SchemaFormatter, - Store: schema.NewSchemaStore(), - } - - Error = types.APISchema{ - Schema: &schemas.Schema{ - ID: "error", - ResourceMethods: []string{}, - CollectionMethods: []string{}, - ResourceFields: map[string]schemas.Field{ - "code": {Type: "string"}, - "detail": {Type: "string", Nullable: true}, - "message": {Type: "string", Nullable: true}, - "fieldName": {Type: "string", Nullable: true}, - "status": {Type: "int"}, - }, - }, - } - - Collection = types.APISchema{ - Schema: &schemas.Schema{ - ID: "collection", - ResourceMethods: []string{}, - CollectionMethods: []string{}, - ResourceFields: map[string]schemas.Field{ - "data": {Type: "array[json]"}, - "pagination": {Type: "map[json]"}, - "sort": {Type: "map[json]"}, - "filters": {Type: "map[json]"}, - }, - }, - } - - Schemas = types.EmptyAPISchemas(). - MustAddSchema(Schema). - MustAddSchema(Error). - MustAddSchema(Collection) -) - -func SchemaFormatter(apiOp *types.APIRequest, resource *types.RawResource) { - schema := apiOp.Schemas.LookupSchema(resource.ID) - if schema == nil { - return - } - - collectionLink := getSchemaCollectionLink(apiOp, schema) - if collectionLink != "" { - resource.Links["collection"] = collectionLink - } -} - -func getSchemaCollectionLink(apiOp *types.APIRequest, schema *types.APISchema) string { - if schema != nil && slice.ContainsString(schema.CollectionMethods, http.MethodGet) { - return apiOp.URLBuilder.Collection(schema) - } - return "" -} diff --git a/pkg/schemaserver/handlers/create.go b/pkg/schemaserver/handlers/create.go deleted file mode 100644 index c992b55..0000000 --- a/pkg/schemaserver/handlers/create.go +++ /dev/null @@ -1,33 +0,0 @@ -package handlers - -import ( - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/parse" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -func CreateHandler(apiOp *types.APIRequest) (types.APIObject, error) { - var err error - - if err := apiOp.AccessControl.CanCreate(apiOp, apiOp.Schema); err != nil { - return types.APIObject{}, err - } - - data, err := parse.Body(apiOp.Request) - if err != nil { - return types.APIObject{}, err - } - - store := apiOp.Schema.Store - if store == nil { - return types.APIObject{}, httperror.NewAPIError(validation.NotFound, "no store found") - } - - data, err = store.Create(apiOp, apiOp.Schema, data) - if err != nil { - return types.APIObject{}, err - } - - return data, nil -} diff --git a/pkg/schemaserver/handlers/delete.go b/pkg/schemaserver/handlers/delete.go deleted file mode 100644 index 2ec787e..0000000 --- a/pkg/schemaserver/handlers/delete.go +++ /dev/null @@ -1,20 +0,0 @@ -package handlers - -import ( - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -func DeleteHandler(request *types.APIRequest) (types.APIObject, error) { - if err := request.AccessControl.CanDelete(request, types.APIObject{}, request.Schema); err != nil { - return types.APIObject{}, err - } - - store := request.Schema.Store - if store == nil { - return types.APIObject{}, httperror.NewAPIError(validation.NotFound, "no store found") - } - - return store.Delete(request, request.Schema, request.Name) -} diff --git a/pkg/schemaserver/handlers/error.go b/pkg/schemaserver/handlers/error.go deleted file mode 100644 index ed72f4a..0000000 --- a/pkg/schemaserver/handlers/error.go +++ /dev/null @@ -1,65 +0,0 @@ -package handlers - -import ( - "net/http" - "net/url" - - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" - "github.com/sirupsen/logrus" -) - -func ErrorHandler(request *types.APIRequest, err error) { - if err == validation.ErrComplete { - return - } - - if ec, ok := err.(validation.ErrorCode); ok { - err = httperror.NewAPIError(ec, "") - } - - var error *httperror.APIError - if apiError, ok := err.(*httperror.APIError); ok { - if apiError.Cause != nil { - url, _ := url.PathUnescape(request.Request.URL.String()) - if url == "" { - url = request.Request.URL.String() - } - logrus.Errorf("API error response %v for %v %v. Cause: %v", apiError.Code.Status, request.Request.Method, - url, apiError.Cause) - } - error = apiError - } else { - logrus.Errorf("Unknown error: %v", err) - error = &httperror.APIError{ - Code: validation.ServerError, - Message: err.Error(), - } - } - - if error.Code.Status == http.StatusNoContent { - request.Response.WriteHeader(http.StatusNoContent) - return - } - - data := toError(error) - request.WriteResponse(error.Code.Status, data) -} - -func toError(apiError *httperror.APIError) types.APIObject { - e := map[string]interface{}{ - "type": "error", - "status": apiError.Code.Status, - "code": apiError.Code.Code, - "message": apiError.Message, - } - if apiError.FieldName != "" { - e["fieldName"] = apiError.FieldName - } - - return types.APIObject{ - Type: "error", - Object: e, - } -} diff --git a/pkg/schemaserver/handlers/list.go b/pkg/schemaserver/handlers/list.go deleted file mode 100644 index fc4c6b6..0000000 --- a/pkg/schemaserver/handlers/list.go +++ /dev/null @@ -1,51 +0,0 @@ -package handlers - -import ( - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -func ByIDHandler(request *types.APIRequest) (types.APIObject, error) { - if err := request.AccessControl.CanGet(request, request.Schema); err != nil { - return types.APIObject{}, err - } - - store := request.Schema.Store - if store == nil { - return types.APIObject{}, httperror.NewAPIError(validation.NotFound, "no store found") - } - - resp, err := store.ByID(request, request.Schema, request.Name) - if err != nil { - return resp, err - } - - if request.Link != "" { - if handler, ok := request.Schema.LinkHandlers[request.Link]; ok { - handler.ServeHTTP(request.Response, request.Request) - return types.APIObject{}, validation.ErrComplete - } - } - - return resp, nil -} - -func ListHandler(request *types.APIRequest) (types.APIObjectList, error) { - if request.Name == "" { - if err := request.AccessControl.CanList(request, request.Schema); err != nil { - return types.APIObjectList{}, err - } - } else { - if err := request.AccessControl.CanGet(request, request.Schema); err != nil { - return types.APIObjectList{}, err - } - } - - store := request.Schema.Store - if store == nil { - return types.APIObjectList{}, httperror.NewAPIError(validation.NotFound, "no store found") - } - - return store.List(request, request.Schema) -} diff --git a/pkg/schemaserver/handlers/update.go b/pkg/schemaserver/handlers/update.go deleted file mode 100644 index 4831ab7..0000000 --- a/pkg/schemaserver/handlers/update.go +++ /dev/null @@ -1,39 +0,0 @@ -package handlers - -import ( - "net/http" - - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/parse" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -func UpdateHandler(apiOp *types.APIRequest) (types.APIObject, error) { - if err := apiOp.AccessControl.CanUpdate(apiOp, types.APIObject{}, apiOp.Schema); err != nil { - return types.APIObject{}, err - } - - var ( - data types.APIObject - err error - ) - if apiOp.Method != http.MethodPatch { - data, err = parse.Body(apiOp.Request) - if err != nil { - return types.APIObject{}, err - } - } - - store := apiOp.Schema.Store - if store == nil { - return types.APIObject{}, httperror.NewAPIError(validation.NotFound, "no store found") - } - - data, err = store.Update(apiOp, apiOp.Schema, data, apiOp.Name) - if err != nil { - return types.APIObject{}, err - } - - return data, nil -} diff --git a/pkg/schemaserver/httperror/error.go b/pkg/schemaserver/httperror/error.go deleted file mode 100644 index 7fefdd1..0000000 --- a/pkg/schemaserver/httperror/error.go +++ /dev/null @@ -1,70 +0,0 @@ -package httperror - -import ( - "fmt" - - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -type APIError struct { - Code validation.ErrorCode - Message string - Cause error - FieldName string -} - -func NewAPIError(code validation.ErrorCode, message string) error { - return &APIError{ - Code: code, - Message: message, - } -} - -func NewFieldAPIError(code validation.ErrorCode, fieldName, message string) error { - return &APIError{ - Code: code, - Message: message, - FieldName: fieldName, - } -} - -// WrapFieldAPIError will cause the API framework to log the underlying err before returning the APIError as a response. -// err WILL NOT be in the API response -func WrapFieldAPIError(err error, code validation.ErrorCode, fieldName, message string) error { - return &APIError{ - Cause: err, - Code: code, - Message: message, - FieldName: fieldName, - } -} - -// WrapAPIError will cause the API framework to log the underlying err before returning the APIError as a response. -// err WILL NOT be in the API response -func WrapAPIError(err error, code validation.ErrorCode, message string) error { - return &APIError{ - Code: code, - Message: message, - Cause: err, - } -} - -func (a *APIError) Error() string { - if a.FieldName != "" { - return fmt.Sprintf("%s=%s: %s", a.FieldName, a.Code, a.Message) - } - return fmt.Sprintf("%s: %s", a.Code, a.Message) -} - -func IsAPIError(err error) bool { - _, ok := err.(*APIError) - return ok -} - -func IsConflict(err error) bool { - if apiError, ok := err.(*APIError); ok { - return apiError.Code.Status == 409 - } - - return false -} diff --git a/pkg/schemaserver/parse/browser.go b/pkg/schemaserver/parse/browser.go deleted file mode 100644 index 6d195d0..0000000 --- a/pkg/schemaserver/parse/browser.go +++ /dev/null @@ -1,18 +0,0 @@ -package parse - -import ( - "net/http" - "strings" -) - -func IsBrowser(req *http.Request, checkAccepts bool) bool { - accepts := strings.ToLower(req.Header.Get("Accept")) - userAgent := strings.ToLower(req.Header.Get("User-Agent")) - - if accepts == "" || !checkAccepts { - accepts = "*/*" - } - - // User agent has Mozilla and browser accepts */* - return strings.Contains(userAgent, "mozilla") && strings.Contains(accepts, "*/*") -} diff --git a/pkg/schemaserver/parse/mux.go b/pkg/schemaserver/parse/mux.go deleted file mode 100644 index 83dd2e2..0000000 --- a/pkg/schemaserver/parse/mux.go +++ /dev/null @@ -1,60 +0,0 @@ -package parse - -import ( - "net/http" - - "github.com/gorilla/mux" - "github.com/rancher/steve/pkg/schemaserver/types" -) - -type Vars struct { - Type string - Name string - Namespace string - Link string - Prefix string - Action string -} - -func Set(v Vars) mux.MatcherFunc { - return func(request *http.Request, match *mux.RouteMatch) bool { - if match.Vars == nil { - match.Vars = map[string]string{} - } - if v.Type != "" { - match.Vars["type"] = v.Type - } - if v.Name != "" { - match.Vars["name"] = v.Name - } - if v.Link != "" { - match.Vars["link"] = v.Link - } - if v.Prefix != "" { - match.Vars["prefix"] = v.Prefix - } - if v.Action != "" { - match.Vars["action"] = v.Action - } - if v.Namespace != "" { - match.Vars["namespace"] = v.Namespace - } - return true - } -} - -func MuxURLParser(rw http.ResponseWriter, req *http.Request, schemas *types.APISchemas) (ParsedURL, error) { - vars := mux.Vars(req) - url := ParsedURL{ - Type: vars["type"], - Name: vars["name"], - Namespace: vars["namespace"], - Link: vars["link"], - Prefix: vars["prefix"], - Method: req.Method, - Action: vars["action"], - Query: req.URL.Query(), - } - - return url, nil -} diff --git a/pkg/schemaserver/parse/parse.go b/pkg/schemaserver/parse/parse.go deleted file mode 100644 index e575e86..0000000 --- a/pkg/schemaserver/parse/parse.go +++ /dev/null @@ -1,172 +0,0 @@ -package parse - -import ( - "net/http" - "net/url" - "strings" - - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/schemaserver/urlbuilder" -) - -const ( - maxFormSize = 2 * 1 << 20 -) - -var ( - allowedFormats = map[string]bool{ - "html": true, - "json": true, - "yaml": true, - } -) - -type ParsedURL struct { - Type string - Name string - Namespace string - Link string - Method string - Action string - Prefix string - SubContext map[string]string - Query url.Values -} - -type URLParser func(rw http.ResponseWriter, req *http.Request, schemas *types.APISchemas) (ParsedURL, error) - -type Parser func(apiOp *types.APIRequest, urlParser URLParser) error - -func Parse(apiOp *types.APIRequest, urlParser URLParser) error { - var err error - - if apiOp.Request == nil { - apiOp.Request, err = http.NewRequest("GET", "/", nil) - if err != nil { - return err - } - } - - apiOp = types.StoreAPIContext(apiOp) - - if apiOp.Method == "" { - apiOp.Method = parseMethod(apiOp.Request) - } - if apiOp.ResponseFormat == "" { - apiOp.ResponseFormat = parseResponseFormat(apiOp.Request) - } - - // The response format is guaranteed to be set even in the event of an error - parsedURL, err := urlParser(apiOp.Response, apiOp.Request, apiOp.Schemas) - // wait to check error, want to set as much as possible - - if apiOp.Type == "" { - apiOp.Type = parsedURL.Type - } - if apiOp.Name == "" { - apiOp.Name = parsedURL.Name - } - if apiOp.Link == "" { - apiOp.Link = parsedURL.Link - } - if apiOp.Action == "" { - apiOp.Action = parsedURL.Action - } - if apiOp.Query == nil { - apiOp.Query = parsedURL.Query - } - if apiOp.Method == "" && parsedURL.Method != "" { - apiOp.Method = parsedURL.Method - } - if apiOp.URLPrefix == "" { - apiOp.URLPrefix = parsedURL.Prefix - } - if apiOp.Namespace == "" { - apiOp.Namespace = parsedURL.Namespace - } - - if apiOp.URLBuilder == nil { - // make error local to not override the outer error we have yet to check - var err error - apiOp.URLBuilder, err = urlbuilder.New(apiOp.Request, &urlbuilder.DefaultPathResolver{ - Prefix: apiOp.URLPrefix, - }, apiOp.Schemas) - if err != nil { - return err - } - } - - if err != nil { - return err - } - - if apiOp.Schema == nil && apiOp.Schemas != nil { - apiOp.Schema = apiOp.Schemas.LookupSchema(apiOp.Type) - } - - if apiOp.Schema != nil && apiOp.Type == "" { - apiOp.Type = apiOp.Schema.ID - } - - if err := ValidateMethod(apiOp); err != nil { - return err - } - - return nil -} - -func parseResponseFormat(req *http.Request) string { - format := req.URL.Query().Get("_format") - - if format != "" { - format = strings.TrimSpace(strings.ToLower(format)) - } - - /* Format specified */ - if allowedFormats[format] { - return format - } - - // User agent has Mozilla and browser accepts */* - if IsBrowser(req, true) { - return "html" - } - - if isYaml(req) { - return "yaml" - } - return "json" -} - -func isYaml(req *http.Request) bool { - return strings.Contains(req.Header.Get("Accept"), "application/yaml") -} - -func parseMethod(req *http.Request) string { - method := req.URL.Query().Get("_method") - if method == "" { - method = req.Method - } - return method -} - -func Body(req *http.Request) (types.APIObject, error) { - req.ParseMultipartForm(maxFormSize) - if req.MultipartForm != nil { - return valuesToBody(req.MultipartForm.Value), nil - } - - if req.PostForm != nil && len(req.PostForm) > 0 { - return valuesToBody(map[string][]string(req.Form)), nil - } - - return ReadBody(req) -} - -func valuesToBody(input map[string][]string) types.APIObject { - result := map[string]interface{}{} - for k, v := range input { - result[k] = v - } - return toAPI(result) -} diff --git a/pkg/schemaserver/parse/read_input.go b/pkg/schemaserver/parse/read_input.go deleted file mode 100644 index a3e0d45..0000000 --- a/pkg/schemaserver/parse/read_input.go +++ /dev/null @@ -1,56 +0,0 @@ -package parse - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/data/convert" - "github.com/rancher/wrangler/pkg/schemas/validation" - "k8s.io/apimachinery/pkg/util/yaml" -) - -const reqMaxSize = (2 * 1 << 20) + 1 - -var bodyMethods = map[string]bool{ - http.MethodPut: true, - http.MethodPost: true, -} - -type Decode func(interface{}) error - -func ReadBody(req *http.Request) (types.APIObject, error) { - if !bodyMethods[req.Method] { - return types.APIObject{}, nil - } - - decode := getDecoder(req, io.LimitReader(req.Body, maxFormSize)) - - data := map[string]interface{}{} - if err := decode(&data); err != nil { - return types.APIObject{}, httperror.NewAPIError(validation.InvalidBodyContent, - fmt.Sprintf("Failed to parse body: %v", err)) - } - - return toAPI(data), nil -} - -func toAPI(data map[string]interface{}) types.APIObject { - return types.APIObject{ - Type: convert.ToString(data["type"]), - ID: convert.ToString(data["id"]), - Object: data, - } -} - -func getDecoder(req *http.Request, reader io.Reader) Decode { - if req.Header.Get("Content-type") == "application/yaml" { - return yaml.NewYAMLToJSONDecoder(reader).Decode - } - decoder := json.NewDecoder(reader) - decoder.UseNumber() - return decoder.Decode -} diff --git a/pkg/schemaserver/parse/validate.go b/pkg/schemaserver/parse/validate.go deleted file mode 100644 index 0de2fdb..0000000 --- a/pkg/schemaserver/parse/validate.go +++ /dev/null @@ -1,47 +0,0 @@ -package parse - -import ( - "fmt" - "net/http" - - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -var ( - supportedMethods = map[string]bool{ - http.MethodPost: true, - http.MethodGet: true, - http.MethodPut: true, - http.MethodPatch: true, - http.MethodDelete: true, - } -) - -func ValidateMethod(request *types.APIRequest) error { - if request.Action != "" && request.Method == http.MethodPost { - return nil - } - - if !supportedMethods[request.Method] { - return httperror.NewAPIError(validation.MethodNotAllowed, fmt.Sprintf("Invalid method %s not supported", request.Method)) - } - - if request.Type == "" || request.Schema == nil || request.Link != "" { - return nil - } - - allowed := request.Schema.ResourceMethods - if request.Name == "" { - allowed = request.Schema.CollectionMethods - } - - for _, method := range allowed { - if method == request.Method { - return nil - } - } - - return httperror.NewAPIError(validation.MethodNotAllowed, fmt.Sprintf("Method %s not supported", request.Method)) -} diff --git a/pkg/schemaserver/server/access.go b/pkg/schemaserver/server/access.go deleted file mode 100644 index 06de94d..0000000 --- a/pkg/schemaserver/server/access.go +++ /dev/null @@ -1,59 +0,0 @@ -package server - -import ( - "net/http" - - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" - "github.com/rancher/wrangler/pkg/slice" -) - -type SchemaBasedAccess struct { -} - -func (*SchemaBasedAccess) CanCreate(apiOp *types.APIRequest, schema *types.APISchema) error { - if slice.ContainsString(schema.CollectionMethods, http.MethodPost) { - return nil - } - return httperror.NewAPIError(validation.PermissionDenied, "can not create "+schema.ID) -} - -func (*SchemaBasedAccess) CanGet(apiOp *types.APIRequest, schema *types.APISchema) error { - if slice.ContainsString(schema.ResourceMethods, http.MethodGet) { - return nil - } - return httperror.NewAPIError(validation.PermissionDenied, "can not get "+schema.ID) -} - -func (*SchemaBasedAccess) CanList(apiOp *types.APIRequest, schema *types.APISchema) error { - if slice.ContainsString(schema.CollectionMethods, http.MethodGet) { - return nil - } - return httperror.NewAPIError(validation.PermissionDenied, "can not list "+schema.ID) -} - -func (*SchemaBasedAccess) CanUpdate(apiOp *types.APIRequest, obj types.APIObject, schema *types.APISchema) error { - if slice.ContainsString(schema.ResourceMethods, http.MethodPut) { - return nil - } - return httperror.NewAPIError(validation.PermissionDenied, "can not update "+schema.ID) -} - -func (*SchemaBasedAccess) CanDelete(apiOp *types.APIRequest, obj types.APIObject, schema *types.APISchema) error { - if slice.ContainsString(schema.ResourceMethods, http.MethodDelete) { - return nil - } - return httperror.NewAPIError(validation.PermissionDenied, "can not delete "+schema.ID) -} - -func (a *SchemaBasedAccess) CanWatch(apiOp *types.APIRequest, schema *types.APISchema) error { - return a.CanList(apiOp, schema) -} - -func (*SchemaBasedAccess) CanAction(apiOp *types.APIRequest, schema *types.APISchema, name string) error { - if _, ok := schema.ActionHandlers[name]; ok { - return httperror.NewAPIError(validation.PermissionDenied, "no such action "+name) - } - return nil -} diff --git a/pkg/schemaserver/server/server.go b/pkg/schemaserver/server/server.go deleted file mode 100644 index e742719..0000000 --- a/pkg/schemaserver/server/server.go +++ /dev/null @@ -1,220 +0,0 @@ -package server - -import ( - "net/http" - - "github.com/rancher/steve/pkg/schemaserver/builtin" - "github.com/rancher/steve/pkg/schemaserver/handlers" - "github.com/rancher/steve/pkg/schemaserver/parse" - "github.com/rancher/steve/pkg/schemaserver/subscribe" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/schemaserver/writer" - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -type RequestHandler interface { - http.Handler - - GetSchemas() *types.APISchemas - Handle(apiOp *types.APIRequest) -} - -type Server struct { - ResponseWriters map[string]types.ResponseWriter - Schemas *types.APISchemas - Defaults Defaults - AccessControl types.AccessControl - Parser parse.Parser - URLParser parse.URLParser -} - -type Defaults struct { - ByIDHandler types.RequestHandler - ListHandler types.RequestListHandler - CreateHandler types.RequestHandler - DeleteHandler types.RequestHandler - UpdateHandler types.RequestHandler - ErrorHandler types.ErrorHandler -} - -func DefaultAPIServer() *Server { - s := &Server{ - Schemas: types.EmptyAPISchemas().MustAddSchemas(builtin.Schemas), - ResponseWriters: map[string]types.ResponseWriter{ - "json": &writer.GzipWriter{ - ResponseWriter: &writer.EncodingResponseWriter{ - ContentType: "application/json", - Encoder: types.JSONEncoder, - }, - }, - "html": &writer.GzipWriter{ - ResponseWriter: &writer.HTMLResponseWriter{ - EncodingResponseWriter: writer.EncodingResponseWriter{ - Encoder: types.JSONEncoder, - ContentType: "application/json", - }, - }, - }, - "yaml": &writer.GzipWriter{ - ResponseWriter: &writer.EncodingResponseWriter{ - ContentType: "application/yaml", - Encoder: types.YAMLEncoder, - }, - }, - }, - AccessControl: &SchemaBasedAccess{}, - Defaults: Defaults{ - ByIDHandler: handlers.ByIDHandler, - CreateHandler: handlers.CreateHandler, - DeleteHandler: handlers.DeleteHandler, - UpdateHandler: handlers.UpdateHandler, - ListHandler: handlers.ListHandler, - ErrorHandler: handlers.ErrorHandler, - }, - Parser: parse.Parse, - URLParser: parse.MuxURLParser, - } - - subscribe.Register(s.Schemas) - return s -} - -func (s *Server) setDefaults(ctx *types.APIRequest) { - if ctx.ResponseWriter == nil { - ctx.ResponseWriter = s.ResponseWriters[ctx.ResponseFormat] - if ctx.ResponseWriter == nil { - ctx.ResponseWriter = s.ResponseWriters["json"] - } - } - - ctx.AccessControl = s.AccessControl - - if ctx.Schemas == nil { - ctx.Schemas = s.Schemas - } -} - -func (s *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - s.Handle(&types.APIRequest{ - Request: req, - Response: rw, - }) -} - -func (s *Server) Handle(apiOp *types.APIRequest) { - s.handle(apiOp, s.Parser) -} - -func (s *Server) handle(apiOp *types.APIRequest, parser parse.Parser) { - if apiOp.Schemas == nil { - apiOp.Schemas = s.Schemas - } - - if err := parser(apiOp, parse.MuxURLParser); err != nil { - // ensure defaults set so writer is assigned - s.setDefaults(apiOp) - s.handleError(apiOp, err) - return - } - - s.setDefaults(apiOp) - - if code, data, err := s.handleOp(apiOp); err != nil { - s.handleError(apiOp, err) - } else if obj, ok := data.(types.APIObject); ok { - apiOp.WriteResponse(code, obj) - } else if list, ok := data.(types.APIObjectList); ok { - apiOp.WriteResponseList(code, list) - } else if code > http.StatusOK { - apiOp.Response.WriteHeader(code) - } -} - -func (s *Server) handleOp(apiOp *types.APIRequest) (int, interface{}, error) { - if err := CheckCSRF(apiOp); err != nil { - return 0, nil, err - } - - action, err := ValidateAction(apiOp) - if err != nil { - return 0, nil, err - } - - if apiOp.Schema == nil { - return http.StatusNotFound, nil, nil - } - - if action != nil { - return http.StatusOK, nil, handleAction(apiOp) - } - - switch apiOp.Method { - case http.MethodGet: - if apiOp.Name == "" { - data, err := handleList(apiOp, apiOp.Schema.ListHandler, s.Defaults.ListHandler) - return http.StatusOK, data, err - } - data, err := handle(apiOp, apiOp.Schema.ByIDHandler, s.Defaults.ByIDHandler) - return http.StatusOK, data, err - case http.MethodPatch: - fallthrough - case http.MethodPut: - data, err := handle(apiOp, apiOp.Schema.UpdateHandler, s.Defaults.UpdateHandler) - return http.StatusOK, data, err - case http.MethodPost: - data, err := handle(apiOp, apiOp.Schema.CreateHandler, s.Defaults.CreateHandler) - return http.StatusCreated, data, err - case http.MethodDelete: - data, err := handle(apiOp, apiOp.Schema.DeleteHandler, s.Defaults.DeleteHandler) - return http.StatusOK, data, err - } - - return http.StatusNotFound, nil, nil -} - -func handleList(apiOp *types.APIRequest, custom types.RequestListHandler, handler types.RequestListHandler) (types.APIObjectList, error) { - if custom != nil { - return custom(apiOp) - } - return handler(apiOp) -} - -func handle(apiOp *types.APIRequest, custom types.RequestHandler, handler types.RequestHandler) (types.APIObject, error) { - if custom != nil { - return custom(apiOp) - } - return handler(apiOp) -} - -func handleAction(context *types.APIRequest) error { - if err := context.AccessControl.CanAction(context, context.Schema, context.Action); err != nil { - return err - } - if handler, ok := context.Schema.ActionHandlers[context.Action]; ok { - handler.ServeHTTP(context.Response, context.Request) - return validation.ErrComplete - } - return nil -} - -func (s *Server) handleError(apiOp *types.APIRequest, err error) { - if apiOp.Schema != nil && apiOp.Schema.ErrorHandler != nil { - apiOp.Schema.ErrorHandler(apiOp, err) - } else if s.Defaults.ErrorHandler != nil { - s.Defaults.ErrorHandler(apiOp, err) - } -} - -func (s *Server) CustomAPIUIResponseWriter(cssURL, jsURL, version writer.StringGetter) { - wi, ok := s.ResponseWriters["html"] - if !ok { - return - } - w, ok := wi.(*writer.HTMLResponseWriter) - if !ok { - return - } - w.CSSURL = cssURL - w.JSURL = jsURL - w.APIUIVersion = version -} diff --git a/pkg/schemaserver/server/validate.go b/pkg/schemaserver/server/validate.go deleted file mode 100644 index 4242266..0000000 --- a/pkg/schemaserver/server/validate.go +++ /dev/null @@ -1,80 +0,0 @@ -package server - -import ( - "crypto/rand" - "encoding/hex" - "fmt" - "net/http" - - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/parse" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas" - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -const ( - csrfCookie = "CSRF" - csrfHeader = "X-API-CSRF" -) - -func ValidateAction(request *types.APIRequest) (*schemas.Action, error) { - if request.Action == "" || request.Link != "" || request.Method != http.MethodPost { - return nil, nil - } - - if err := request.AccessControl.CanAction(request, request.Schema, request.Action); err != nil { - return nil, err - } - - actions := request.Schema.CollectionActions - if request.Name != "" { - actions = request.Schema.ResourceActions - } - - action, ok := actions[request.Action] - if !ok { - return nil, httperror.NewAPIError(validation.InvalidAction, fmt.Sprintf("Invalid action: %s", request.Action)) - } - - return &action, nil -} - -func CheckCSRF(apiOp *types.APIRequest) error { - if !parse.IsBrowser(apiOp.Request, false) { - return nil - } - - cookie, err := apiOp.Request.Cookie(csrfCookie) - if err == http.ErrNoCookie { - bytes := make([]byte, 5) - _, err := rand.Read(bytes) - if err != nil { - return httperror.WrapAPIError(err, validation.ServerError, "Failed in CSRF processing") - } - - cookie = &http.Cookie{ - Name: csrfCookie, - Value: hex.EncodeToString(bytes), - Path: "/", - Secure: true, - } - - http.SetCookie(apiOp.Response, cookie) - } else if err != nil { - return httperror.NewAPIError(validation.InvalidCSRFToken, "Failed to parse cookies") - } else if apiOp.Method != http.MethodGet { - /* - * Very important to use apiOp.Method and not apiOp.Request.Method. The client can override the HTTP method with _method - */ - if cookie.Value == apiOp.Request.Header.Get(csrfHeader) { - // Good - } else if cookie.Value == apiOp.Request.URL.Query().Get(csrfCookie) { - // Good - } else { - return httperror.NewAPIError(validation.InvalidCSRFToken, "Invalid CSRF token") - } - } - - return nil -} diff --git a/pkg/schemaserver/store/apiroot/apiroot.go b/pkg/schemaserver/store/apiroot/apiroot.go deleted file mode 100644 index 2bc8be7..0000000 --- a/pkg/schemaserver/store/apiroot/apiroot.go +++ /dev/null @@ -1,135 +0,0 @@ -package apiroot - -import ( - "net/http" - "strings" - - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas" -) - -func Register(apiSchemas *types.APISchemas, versions, roots []string) { - apiSchemas.MustAddSchema(types.APISchema{ - Schema: &schemas.Schema{ - ID: "apiRoot", - CollectionMethods: []string{"GET"}, - ResourceMethods: []string{"GET"}, - ResourceFields: map[string]schemas.Field{ - "apiVersion": {Type: "map[json]"}, - "path": {Type: "string"}, - }, - }, - Formatter: Formatter, - Store: NewAPIRootStore(versions, roots), - }) -} - -func Formatter(apiOp *types.APIRequest, resource *types.RawResource) { - data := resource.APIObject.Data() - path, _ := data["path"].(string) - if path == "" { - return - } - delete(data, "path") - - resource.Links["root"] = apiOp.URLBuilder.RelativeToRoot(path) - - if data, isAPIRoot := data["apiVersion"].(map[string]interface{}); isAPIRoot { - apiVersion := apiVersionFromMap(apiOp.Schemas, data) - resource.Links["self"] = apiOp.URLBuilder.RelativeToRoot(apiVersion) - - resource.Links["schemas"] = apiOp.URLBuilder.RelativeToRoot(path) - for _, schema := range apiOp.Schemas.Schemas { - addCollectionLink(apiOp, schema, resource.Links) - } - } - - return -} - -func addCollectionLink(apiOp *types.APIRequest, schema *types.APISchema, links map[string]string) { - collectionLink := getSchemaCollectionLink(apiOp, schema) - if collectionLink != "" { - links[schema.PluralName] = collectionLink - } -} - -func getSchemaCollectionLink(apiOp *types.APIRequest, schema *types.APISchema) string { - if schema != nil && contains(schema.CollectionMethods, http.MethodGet) { - return apiOp.URLBuilder.Collection(schema) - } - return "" -} - -type Store struct { - empty.Store - roots []string - versions []string -} - -func NewAPIRootStore(versions []string, roots []string) types.Store { - return &Store{ - roots: roots, - versions: versions, - } -} - -func (a *Store) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) { - return types.DefaultByID(a, apiOp, schema, id) -} - -func (a *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) { - var roots types.APIObjectList - - versions := a.versions - - for _, version := range versions { - roots.Objects = append(roots.Objects, types.APIObject{ - Type: "apiRoot", - ID: version, - Object: apiVersionToAPIRootMap(version), - }) - } - - for _, root := range a.roots { - parts := strings.SplitN(root, ":", 2) - if len(parts) == 2 { - roots.Objects = append(roots.Objects, types.APIObject{ - Type: "apiRoot", - ID: parts[0], - Object: map[string]interface{}{ - "id": parts[0], - "path": parts[1], - }, - }) - } - } - - return roots, nil -} - -func apiVersionToAPIRootMap(version string) map[string]interface{} { - return map[string]interface{}{ - "id": version, - "type": "apiRoot", - "apiVersion": map[string]interface{}{ - "version": version, - }, - "path": "/" + version, - } -} - -func apiVersionFromMap(schemas *types.APISchemas, apiVersion map[string]interface{}) string { - version, _ := apiVersion["version"].(string) - return version -} - -func contains(list []string, needle string) bool { - for _, v := range list { - if v == needle { - return true - } - } - return false -} diff --git a/pkg/schemaserver/store/empty/empty_store.go b/pkg/schemaserver/store/empty/empty_store.go deleted file mode 100644 index 47d49a2..0000000 --- a/pkg/schemaserver/store/empty/empty_store.go +++ /dev/null @@ -1,33 +0,0 @@ -package empty - -import ( - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" -) - -type Store struct { -} - -func (e *Store) Delete(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) { - return types.APIObject{}, validation.NotFound -} - -func (e *Store) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) { - return types.APIObject{}, validation.NotFound -} - -func (e *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) { - return types.APIObjectList{}, validation.NotFound -} - -func (e *Store) Create(apiOp *types.APIRequest, schema *types.APISchema, data types.APIObject) (types.APIObject, error) { - return types.APIObject{}, validation.NotFound -} - -func (e *Store) Update(apiOp *types.APIRequest, schema *types.APISchema, data types.APIObject, id string) (types.APIObject, error) { - return types.APIObject{}, validation.NotFound -} - -func (e *Store) Watch(apiOp *types.APIRequest, schema *types.APISchema, wr types.WatchRequest) (chan types.APIEvent, error) { - return nil, nil -} diff --git a/pkg/schemaserver/store/schema/schema_store.go b/pkg/schemaserver/store/schema/schema_store.go deleted file mode 100644 index ed643ad..0000000 --- a/pkg/schemaserver/store/schema/schema_store.go +++ /dev/null @@ -1,105 +0,0 @@ -package schema - -import ( - "github.com/rancher/wrangler/pkg/schemas/validation" - - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/store/empty" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/definition" -) - -type Store struct { - empty.Store -} - -func NewSchemaStore() types.Store { - return &Store{} -} - -func toAPIObject(schema *types.APISchema) types.APIObject { - s := schema.DeepCopy() - delete(s.Schema.Attributes, "access") - return types.APIObject{ - Type: "schema", - ID: schema.ID, - Object: s, - } -} - -func (s *Store) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) { - schema = apiOp.Schemas.LookupSchema(id) - if schema == nil { - return types.APIObject{}, httperror.NewAPIError(validation.NotFound, "no such schema") - } - return toAPIObject(schema), nil -} - -func (s *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) { - return FilterSchemas(apiOp, apiOp.Schemas.Schemas), nil -} - -func FilterSchemas(apiOp *types.APIRequest, schemaMap map[string]*types.APISchema) types.APIObjectList { - schemas := types.APIObjectList{} - - included := map[string]bool{} - for _, schema := range schemaMap { - if included[schema.ID] { - continue - } - - if apiOp.AccessControl.CanList(apiOp, schema) == nil || apiOp.AccessControl.CanGet(apiOp, schema) == nil { - schemas = addSchema(apiOp, schema, schemaMap, schemas, included) - } - } - - return schemas -} - -func addSchema(apiOp *types.APIRequest, schema *types.APISchema, schemaMap map[string]*types.APISchema, schemas types.APIObjectList, included map[string]bool) types.APIObjectList { - included[schema.ID] = true - schemas = traverseAndAdd(apiOp, schema, schemaMap, schemas, included) - schemas.Objects = append(schemas.Objects, toAPIObject(schema)) - return schemas -} - -func traverseAndAdd(apiOp *types.APIRequest, schema *types.APISchema, schemaMap map[string]*types.APISchema, schemas types.APIObjectList, included map[string]bool) types.APIObjectList { - for _, field := range schema.ResourceFields { - t := "" - subType := field.Type - for subType != t { - t = subType - subType = definition.SubType(t) - } - - if refSchema, ok := schemaMap[t]; ok && !included[t] { - schemas = addSchema(apiOp, refSchema, schemaMap, schemas, included) - } - } - - for _, action := range schema.ResourceActions { - for _, t := range []string{action.Output, action.Input} { - if t == "" { - continue - } - - if refSchema, ok := schemaMap[t]; ok && !included[t] { - schemas = addSchema(apiOp, refSchema, schemaMap, schemas, included) - } - } - } - - for _, action := range schema.CollectionActions { - for _, t := range []string{action.Output, action.Input} { - if t == "" { - continue - } - - if refSchema, ok := schemaMap[t]; ok && !included[t] { - schemas = addSchema(apiOp, refSchema, schemaMap, schemas, included) - } - } - } - - return schemas -} diff --git a/pkg/schemaserver/subscribe/convert.go b/pkg/schemaserver/subscribe/convert.go deleted file mode 100644 index 06c3fd5..0000000 --- a/pkg/schemaserver/subscribe/convert.go +++ /dev/null @@ -1,53 +0,0 @@ -package subscribe - -import ( - "io" - - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/schemaserver/writer" -) - -type Converter struct { - writer.EncodingResponseWriter - apiOp *types.APIRequest - obj interface{} -} - -func MarshallObject(apiOp *types.APIRequest, event types.APIEvent) types.APIEvent { - if event.Error != nil { - return event - } - - data, err := newConverter(apiOp).ToAPIObject(event.Object) - if err != nil { - event.Error = err - return event - } - - event.Data = data - return event -} - -func newConverter(apiOp *types.APIRequest) *Converter { - c := &Converter{ - apiOp: apiOp, - } - c.EncodingResponseWriter = writer.EncodingResponseWriter{ - ContentType: "application/json", - Encoder: c.Encoder, - } - return c -} - -func (c *Converter) ToAPIObject(data types.APIObject) (interface{}, error) { - c.obj = nil - if err := c.Body(c.apiOp, nil, data); err != nil { - return types.APIObject{}, err - } - return c.obj, nil -} - -func (c *Converter) Encoder(_ io.Writer, obj interface{}) error { - c.obj = obj - return nil -} diff --git a/pkg/schemaserver/subscribe/handler.go b/pkg/schemaserver/subscribe/handler.go deleted file mode 100644 index 29398a5..0000000 --- a/pkg/schemaserver/subscribe/handler.go +++ /dev/null @@ -1,79 +0,0 @@ -package subscribe - -import ( - "encoding/json" - "time" - - "github.com/gorilla/websocket" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/schemas/validation" - "github.com/sirupsen/logrus" -) - -var upgrader = websocket.Upgrader{ - HandshakeTimeout: 60 * time.Second, - EnableCompression: true, -} - -type Subscribe struct { - Stop bool `json:"stop,omitempty"` - ResourceType string `json:"resourceType,omitempty"` - ResourceVersion string `json:"resourceVersion,omitempty"` -} - -func Handler(apiOp *types.APIRequest) (types.APIObjectList, error) { - err := handler(apiOp) - if err != nil { - logrus.Errorf("Error during subscribe %v", err) - } - return types.APIObjectList{}, validation.ErrComplete -} - -func handler(apiOp *types.APIRequest) error { - c, err := upgrader.Upgrade(apiOp.Response, apiOp.Request, nil) - if err != nil { - return err - } - defer c.Close() - - watches := NewWatchSession(apiOp) - defer watches.Close() - - events := watches.Watch(c) - t := time.NewTicker(30 * time.Second) - defer t.Stop() - - for { - select { - case event, ok := <-events: - if !ok { - return nil - } - if err := writeData(apiOp, c, event); err != nil { - return err - } - case <-t.C: - if err := writeData(apiOp, c, types.APIEvent{Name: "ping"}); err != nil { - return err - } - } - } -} - -func writeData(apiOp *types.APIRequest, c *websocket.Conn, event types.APIEvent) error { - event = MarshallObject(apiOp, event) - if event.Error != nil { - event.Name = "resource.error" - event.Data = map[string]interface{}{ - "error": event.Error.Error(), - } - } - - messageWriter, err := c.NextWriter(websocket.TextMessage) - if err != nil { - return err - } - defer messageWriter.Close() - - return json.NewEncoder(messageWriter).Encode(event) -} diff --git a/pkg/schemaserver/subscribe/register.go b/pkg/schemaserver/subscribe/register.go deleted file mode 100644 index 458deb4..0000000 --- a/pkg/schemaserver/subscribe/register.go +++ /dev/null @@ -1,16 +0,0 @@ -package subscribe - -import ( - "net/http" - - "github.com/rancher/steve/pkg/schemaserver/types" -) - -func Register(schemas *types.APISchemas) { - schemas.MustImportAndCustomize(Subscribe{}, func(schema *types.APISchema) { - schema.CollectionMethods = []string{http.MethodGet} - schema.ResourceMethods = []string{} - schema.ListHandler = Handler - schema.PluralName = "subscribe" - }) -} diff --git a/pkg/schemaserver/subscribe/watcher.go b/pkg/schemaserver/subscribe/watcher.go deleted file mode 100644 index a08f46e..0000000 --- a/pkg/schemaserver/subscribe/watcher.go +++ /dev/null @@ -1,145 +0,0 @@ -package subscribe - -import ( - "context" - "encoding/json" - "fmt" - "sync" - - "github.com/gorilla/websocket" - "github.com/rancher/steve/pkg/schemaserver/types" -) - -type WatchSession struct { - sync.Mutex - - apiOp *types.APIRequest - watchers map[string]func() - wg sync.WaitGroup - ctx context.Context - cancel func() -} - -func (s *WatchSession) stop(id string, resp chan<- types.APIEvent) { - s.Lock() - defer s.Unlock() - if cancel, ok := s.watchers[id]; ok { - cancel() - resp <- types.APIEvent{ - Name: "resource.stop", - ResourceType: id, - } - } - delete(s.watchers, id) -} - -func (s *WatchSession) add(resourceType, revision string, resp chan<- types.APIEvent) { - s.Lock() - defer s.Unlock() - - ctx, cancel := context.WithCancel(s.ctx) - s.watchers[resourceType] = cancel - - s.wg.Add(1) - go func() { - defer s.wg.Done() - defer s.stop(resourceType, resp) - - if err := s.stream(ctx, resourceType, revision, resp); err != nil { - sendErr(resp, err, resourceType) - } - }() -} - -func (s *WatchSession) stream(ctx context.Context, resourceType, revision string, result chan<- types.APIEvent) error { - schema := s.apiOp.Schemas.LookupSchema(resourceType) - if schema == nil { - return fmt.Errorf("failed to find schema %s", resourceType) - } else if schema.Store == nil { - return fmt.Errorf("schema %s does not support watching", resourceType) - } - - if err := s.apiOp.AccessControl.CanWatch(s.apiOp, schema); err != nil { - return err - } - - c, err := schema.Store.Watch(s.apiOp.WithContext(ctx), schema, types.WatchRequest{Revision: revision}) - if err != nil { - return err - } - - result <- types.APIEvent{ - Name: "resource.start", - ResourceType: resourceType, - } - - for event := range c { - result <- event - } - - return nil -} - -func NewWatchSession(apiOp *types.APIRequest) *WatchSession { - ws := &WatchSession{ - apiOp: apiOp, - watchers: map[string]func(){}, - } - - ws.ctx, ws.cancel = context.WithCancel(apiOp.Request.Context()) - return ws -} - -func (s *WatchSession) Watch(conn *websocket.Conn) <-chan types.APIEvent { - result := make(chan types.APIEvent, 100) - go func() { - defer close(result) - - if err := s.watch(conn, result); err != nil { - sendErr(result, err, "") - } - }() - return result -} - -func (s *WatchSession) Close() { - s.cancel() - s.wg.Wait() -} - -func (s *WatchSession) watch(conn *websocket.Conn, resp chan types.APIEvent) error { - defer s.wg.Wait() - defer s.cancel() - - for { - _, r, err := conn.NextReader() - if err != nil { - return err - } - - var sub Subscribe - - if err := json.NewDecoder(r).Decode(&sub); err != nil { - sendErr(resp, err, "") - continue - } - - if sub.Stop { - s.stop(sub.ResourceType, resp) - } else { - s.Lock() - _, ok := s.watchers[sub.ResourceType] - s.Unlock() - if !ok { - s.add(sub.ResourceType, sub.ResourceVersion, resp) - } - } - } -} - -func sendErr(resp chan<- types.APIEvent, err error, resourceType string) { - resp <- types.APIEvent{ - ResourceType: resourceType, - Error: err, - } -} diff --git a/pkg/schemaserver/types/encoder.go b/pkg/schemaserver/types/encoder.go deleted file mode 100644 index 0dcf562..0000000 --- a/pkg/schemaserver/types/encoder.go +++ /dev/null @@ -1,25 +0,0 @@ -package types - -import ( - "encoding/json" - "io" - - "github.com/ghodss/yaml" -) - -func JSONEncoder(writer io.Writer, v interface{}) error { - return json.NewEncoder(writer).Encode(v) -} - -func YAMLEncoder(writer io.Writer, v interface{}) error { - data, err := json.Marshal(v) - if err != nil { - return err - } - buf, err := yaml.JSONToYAML(data) - if err != nil { - return err - } - _, err = writer.Write(buf) - return err -} diff --git a/pkg/schemaserver/types/schemas.go b/pkg/schemaserver/types/schemas.go deleted file mode 100644 index a67a77e..0000000 --- a/pkg/schemaserver/types/schemas.go +++ /dev/null @@ -1,96 +0,0 @@ -package types - -import ( - "strings" - - "github.com/rancher/wrangler/pkg/schemas" - "github.com/sirupsen/logrus" -) - -type APISchemas struct { - InternalSchemas *schemas.Schemas - Schemas map[string]*APISchema - index map[string]*APISchema -} - -func EmptyAPISchemas() *APISchemas { - return &APISchemas{ - InternalSchemas: schemas.EmptySchemas(), - Schemas: map[string]*APISchema{}, - index: map[string]*APISchema{}, - } -} - -func (a *APISchemas) MustAddSchema(obj APISchema) *APISchemas { - err := a.AddSchema(obj) - if err != nil { - logrus.Fatalf("failed to add schema: %v", err) - } - return a -} - -func (a *APISchemas) addInternalSchema(schema *schemas.Schema) *APISchema { - apiSchema := &APISchema{ - Schema: schema, - } - a.Schemas[schema.ID] = apiSchema - a.addToIndex(apiSchema) - - for _, f := range schema.ResourceFields { - if subType := a.InternalSchemas.Schema(f.Type); subType == nil { - continue - } else if _, ok := a.Schemas[subType.ID]; !ok { - a.addInternalSchema(subType) - } - } - - return apiSchema -} - -func (a *APISchemas) MustImportAndCustomize(obj interface{}, f func(*APISchema)) { - schema, err := a.InternalSchemas.Import(obj) - if err != nil { - panic(err) - } - apiSchema := a.addInternalSchema(schema) - f(apiSchema) -} - -func (a *APISchemas) MustAddSchemas(schemas *APISchemas) *APISchemas { - if err := a.AddSchemas(schemas); err != nil { - logrus.Fatalf("failed to add schemas: %v", err) - } - return a -} - -func (a *APISchemas) AddSchemas(schema *APISchemas) error { - for _, schema := range schema.Schemas { - if err := a.AddSchema(*schema); err != nil { - return err - } - } - return nil -} - -func (a *APISchemas) addToIndex(schema *APISchema) { - a.index[strings.ToLower(schema.ID)] = schema - a.index[strings.ToLower(schema.PluralName)] = schema -} - -func (a *APISchemas) AddSchema(schema APISchema) error { - if err := a.InternalSchemas.AddSchema(*schema.Schema); err != nil { - return err - } - schema.Schema = a.InternalSchemas.Schema(schema.ID) - a.Schemas[schema.ID] = &schema - a.addToIndex(&schema) - return nil -} - -func (a *APISchemas) LookupSchema(name string) *APISchema { - s, ok := a.Schemas[name] - if ok { - return s - } - return a.index[strings.ToLower(name)] -} diff --git a/pkg/schemaserver/types/server_types.go b/pkg/schemaserver/types/server_types.go deleted file mode 100644 index a985e7f..0000000 --- a/pkg/schemaserver/types/server_types.go +++ /dev/null @@ -1,295 +0,0 @@ -package types - -import ( - "context" - "encoding/json" - "net/http" - "net/url" - - "github.com/rancher/wrangler/pkg/data" - "github.com/rancher/wrangler/pkg/data/convert" - "github.com/rancher/wrangler/pkg/schemas/validation" - meta2 "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/apiserver/pkg/endpoints/request" -) - -type RawResource struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Type string `json:"type,omitempty" yaml:"type,omitempty"` - Schema *APISchema `json:"-" yaml:"-"` - Links map[string]string `json:"links" yaml:"links,omitempty"` - Actions map[string]string `json:"actions,omitempty" yaml:"actions,omitempty"` - ActionLinks bool `json:"-" yaml:"-"` - APIObject APIObject `json:"-" yaml:"-"` -} - -type Pagination struct { - Limit int `json:"limit,omitempty"` - First string `json:"first,omitempty"` - Next string `json:"next,omitempty"` - Partial bool `json:"partial,omitempty"` -} - -func (r *RawResource) MarshalJSON() ([]byte, error) { - type r_ RawResource - outer, err := json.Marshal((*r_)(r)) - if err != nil { - return nil, err - } - - last := len(outer) - 1 - if len(outer) < 2 || outer[last] != '}' { - return outer, nil - } - - data, err := json.Marshal(r.APIObject.Object) - if err != nil { - return nil, err - } - - if len(data) < 3 || data[0] != '{' || data[len(data)-1] != '}' { - return outer, nil - } - - if outer[last-1] == '{' { - outer[last] = ' ' - } else { - outer[last] = ',' - } - - return append(outer, data[1:]...), nil -} - -func (r *RawResource) AddAction(apiOp *APIRequest, name string) { - r.Actions[name] = apiOp.URLBuilder.Action(r.Schema, r.ID, name) -} - -type RequestHandler func(request *APIRequest) (APIObject, error) - -type RequestListHandler func(request *APIRequest) (APIObjectList, error) - -type Formatter func(request *APIRequest, resource *RawResource) - -type CollectionFormatter func(request *APIRequest, collection *GenericCollection) - -type ErrorHandler func(request *APIRequest, err error) - -type ResponseWriter interface { - Write(apiOp *APIRequest, code int, obj APIObject) - WriteList(apiOp *APIRequest, code int, obj APIObjectList) -} - -type AccessControl interface { - CanAction(apiOp *APIRequest, schema *APISchema, name string) error - CanCreate(apiOp *APIRequest, schema *APISchema) error - CanList(apiOp *APIRequest, schema *APISchema) error - CanGet(apiOp *APIRequest, schema *APISchema) error - CanUpdate(apiOp *APIRequest, obj APIObject, schema *APISchema) error - CanDelete(apiOp *APIRequest, obj APIObject, schema *APISchema) error - CanWatch(apiOp *APIRequest, schema *APISchema) error -} - -type APIRequest struct { - Action string - Name string - Type string - Link string - Method string - Namespace string - Schema *APISchema - Schemas *APISchemas - Query url.Values - ResponseFormat string - ResponseWriter ResponseWriter - URLPrefix string - URLBuilder URLBuilder - AccessControl AccessControl - - Request *http.Request - Response http.ResponseWriter -} - -type apiOpKey struct{} - -func GetAPIContext(ctx context.Context) *APIRequest { - apiOp, _ := ctx.Value(apiOpKey{}).(*APIRequest) - return apiOp -} - -func StoreAPIContext(apiOp *APIRequest) *APIRequest { - ctx := context.WithValue(apiOp.Request.Context(), apiOpKey{}, apiOp) - apiOp.Request = apiOp.Request.WithContext(ctx) - return apiOp -} - -func (r *APIRequest) WithContext(ctx context.Context) *APIRequest { - result := *r - result.Request = result.Request.WithContext(ctx) - return &result -} - -func (r *APIRequest) Context() context.Context { - return r.Request.Context() -} - -func (r *APIRequest) GetUser() string { - user, ok := request.UserFrom(r.Request.Context()) - if ok { - return user.GetName() - } - return "" -} - -func (r *APIRequest) GetUserInfo() (user.Info, bool) { - return request.UserFrom(r.Request.Context()) -} - -func (r *APIRequest) Option(key string) string { - return r.Query.Get("_" + key) -} - -func (r *APIRequest) WriteResponse(code int, obj APIObject) { - r.ResponseWriter.Write(r, code, obj) -} - -func (r *APIRequest) WriteResponseList(code int, list APIObjectList) { - r.ResponseWriter.WriteList(r, code, list) -} - -type URLBuilder interface { - Current() string - - Collection(schema *APISchema) string - CollectionAction(schema *APISchema, action string) string - ResourceLink(schema *APISchema, id string) string - Link(schema *APISchema, id string, linkName string) string - Action(schema *APISchema, id string, action string) string - Marker(marker string) string - - RelativeToRoot(path string) string -} - -type Store interface { - ByID(apiOp *APIRequest, schema *APISchema, id string) (APIObject, error) - List(apiOp *APIRequest, schema *APISchema) (APIObjectList, error) - Create(apiOp *APIRequest, schema *APISchema, data APIObject) (APIObject, error) - Update(apiOp *APIRequest, schema *APISchema, data APIObject, id string) (APIObject, error) - Delete(apiOp *APIRequest, schema *APISchema, id string) (APIObject, error) - Watch(apiOp *APIRequest, schema *APISchema, w WatchRequest) (chan APIEvent, error) -} - -func DefaultByID(store Store, apiOp *APIRequest, schema *APISchema, id string) (APIObject, error) { - list, err := store.List(apiOp, schema) - if err != nil { - return APIObject{}, err - } - - for _, item := range list.Objects { - if item.ID == id { - return item, nil - } - } - - return APIObject{}, validation.NotFound -} - -type WatchRequest struct { - Revision string -} - -var ( - ChangeAPIEvent = "resource.change" - RemoveAPIEvent = "resource.remove" - CreateAPIEvent = "resource.create" -) - -type APIEvent struct { - Name string `json:"name,omitempty"` - ResourceType string `json:"resourceType,omitempty"` - Revision string `json:"revision,omitempty"` - Object APIObject `json:"-"` - Error error `json:"-"` - // Data is the output format of the object - Data interface{} `json:"data,omitempty"` -} - -type APIObject struct { - Type string - ID string - Object interface{} -} - -type APIObjectList struct { - Revision string - Continue string - Objects []APIObject -} - -func (a *APIObject) Data() data.Object { - if unstr, ok := a.Object.(*unstructured.Unstructured); ok { - return unstr.Object - } - data, err := convert.EncodeToMap(a.Object) - if err != nil { - return convert.ToMapInterface(a.Object) - } - return data -} - -func (a *APIObject) Name() string { - if ro, ok := a.Object.(runtime.Object); ok { - meta, err := meta2.Accessor(ro) - if err == nil { - return meta.GetName() - } - } - return Name(a.Data()) -} - -func (a *APIObject) Namespace() string { - if ro, ok := a.Object.(runtime.Object); ok { - meta, err := meta2.Accessor(ro) - if err == nil { - return meta.GetNamespace() - } - } - return Namespace(a.Data()) -} - -func Name(d map[string]interface{}) string { - return convert.ToString(data.GetValueN(d, "metadata", "name")) -} - -func Namespace(d map[string]interface{}) string { - return convert.ToString(data.GetValueN(d, "metadata", "namespace")) -} - -func APIChan(c <-chan APIEvent, f func(APIObject) APIObject) chan APIEvent { - if c == nil { - return nil - } - result := make(chan APIEvent) - go func() { - for data := range c { - data.Object = f(data.Object) - result <- data - } - close(result) - }() - return result -} - -func FormatterChain(formatter Formatter, next Formatter) Formatter { - return func(request *APIRequest, resource *RawResource) { - formatter(request, resource) - next(request, resource) - } -} - -func (r *APIRequest) Clone() *APIRequest { - clone := *r - return &clone -} diff --git a/pkg/schemaserver/types/types.go b/pkg/schemaserver/types/types.go deleted file mode 100644 index 3549268..0000000 --- a/pkg/schemaserver/types/types.go +++ /dev/null @@ -1,103 +0,0 @@ -package types - -import ( - "net/http" - - "github.com/rancher/wrangler/pkg/schemas" -) - -type Collection struct { - Type string `json:"type,omitempty"` - Links map[string]string `json:"links"` - CreateTypes map[string]string `json:"createTypes,omitempty"` - Actions map[string]string `json:"actions"` - ResourceType string `json:"resourceType"` - Pagination *Pagination `json:"pagination,omitempty"` - Revision string `json:"revision,omitempty"` - Continue string `json:"continue,omitempty"` -} - -type GenericCollection struct { - Collection - Data []*RawResource `json:"data"` -} - -var ( - ModifierEQ ModifierType = "eq" - ModifierNE ModifierType = "ne" - ModifierNull ModifierType = "null" - ModifierNotNull ModifierType = "notnull" - ModifierIn ModifierType = "in" - ModifierNotIn ModifierType = "notin" -) - -type ModifierType string - -type Condition struct { - Modifier ModifierType `json:"modifier,omitempty"` - Value interface{} `json:"value,omitempty"` -} - -type Resource struct { - ID string `json:"id,omitempty"` - Type string `json:"type,omitempty"` - Links map[string]string `json:"links"` - Actions map[string]string `json:"actions"` -} - -type NamedResource struct { - Resource - Name string `json:"name"` - Description string `json:"description"` -} - -type NamedResourceCollection struct { - Collection - Data []NamedResource `json:"data,omitempty"` -} - -var ReservedFields = map[string]bool{ - "id": true, - "type": true, - "links": true, - "actions": true, -} - -type APISchema struct { - *schemas.Schema - - ActionHandlers map[string]http.Handler `json:"-"` - LinkHandlers map[string]http.Handler `json:"-"` - ListHandler RequestListHandler `json:"-"` - ByIDHandler RequestHandler `json:"-"` - CreateHandler RequestHandler `json:"-"` - DeleteHandler RequestHandler `json:"-"` - UpdateHandler RequestHandler `json:"-"` - Formatter Formatter `json:"-"` - CollectionFormatter CollectionFormatter `json:"-"` - ErrorHandler ErrorHandler `json:"-"` - Store Store `json:"-"` -} - -func copyHandlers(m map[string]http.Handler) map[string]http.Handler { - if m == nil { - return nil - } - result := make(map[string]http.Handler, len(m)) - for k, v := range m { - result[k] = v - } - - return result -} -func (a *APISchema) DeepCopy() *APISchema { - r := *a - r.ActionHandlers = copyHandlers(a.ActionHandlers) - r.LinkHandlers = copyHandlers(a.ActionHandlers) - r.Schema = r.Schema.DeepCopy() - return &r -} - -func (c *Collection) AddAction(apiOp *APIRequest, name string) { - c.Actions[name] = apiOp.URLBuilder.CollectionAction(apiOp.Schema, name) -} diff --git a/pkg/schemaserver/urlbuilder/base.go b/pkg/schemaserver/urlbuilder/base.go deleted file mode 100644 index bffcefe..0000000 --- a/pkg/schemaserver/urlbuilder/base.go +++ /dev/null @@ -1,67 +0,0 @@ -package urlbuilder - -import ( - "bytes" - "fmt" - "net/http" - "net/url" - "strings" -) - -func ParseRequestURL(r *http.Request) string { - scheme := GetScheme(r) - host := GetHost(r, scheme) - return fmt.Sprintf("%s://%s%s%s", scheme, host, r.Header.Get(PrefixHeader), r.URL.Path) -} - -func GetHost(r *http.Request, scheme string) string { - host := r.Header.Get(ForwardedAPIHostHeader) - if host != "" { - return host - } - - host = strings.Split(r.Header.Get(ForwardedHostHeader), ",")[0] - if host != "" { - return host - } - - return r.Host -} - -func GetScheme(r *http.Request) string { - scheme := r.Header.Get(ForwardedProtoHeader) - if scheme != "" { - switch scheme { - case "ws": - return "http" - case "wss": - return "https" - default: - return scheme - } - } else if r.TLS != nil { - return "https" - } - return "http" -} - -func ParseResponseURLBase(currentURL string, r *http.Request) (string, error) { - path := r.URL.Path - - index := strings.LastIndex(currentURL, path) - if index == -1 { - // Fallback, if we can't find path in currentURL, then we just assume the base is the root of the web request - u, err := url.Parse(currentURL) - if err != nil { - return "", err - } - - buffer := bytes.Buffer{} - buffer.WriteString(u.Scheme) - buffer.WriteString("://") - buffer.WriteString(u.Host) - return buffer.String(), nil - } - - return currentURL[0:index], nil -} diff --git a/pkg/schemaserver/urlbuilder/redirect.go b/pkg/schemaserver/urlbuilder/redirect.go deleted file mode 100644 index d4a765c..0000000 --- a/pkg/schemaserver/urlbuilder/redirect.go +++ /dev/null @@ -1,81 +0,0 @@ -package urlbuilder - -import ( - "bytes" - "net/http" - "net/url" - "strings" -) - -func RedirectRewrite(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - prefix := req.Header.Get(PrefixHeader) - if prefix == "" { - next.ServeHTTP(rw, req) - return - } - r := &redirector{ - ResponseWriter: rw, - prefix: prefix, - } - if h, ok := rw.(http.Hijacker); ok { - r.Hijacker = h - } - next.ServeHTTP(r, req) - r.Close() - }) -} - -type redirector struct { - http.ResponseWriter - http.Hijacker - prefix string - from, to string - tempBuffer *bytes.Buffer -} - -func (r *redirector) Write(content []byte) (int, error) { - if r.tempBuffer == nil { - return r.ResponseWriter.Write(content) - } - return r.tempBuffer.Write(content) -} - -func (r *redirector) Close() error { - if r.tempBuffer == nil || r.from == "" || r.to == "" { - return nil - } - - content := bytes.Replace(r.tempBuffer.Bytes(), []byte(r.from), []byte(r.to), -1) - _, err := r.ResponseWriter.Write(content) - r.tempBuffer = nil - return err -} - -func (r *redirector) WriteHeader(statusCode int) { - defer func() { - // the anonymous func is so that we take the new value of statusCode, - // not copy it at invocation - r.ResponseWriter.WriteHeader(statusCode) - }() - - if statusCode != http.StatusMovedPermanently && statusCode != http.StatusFound { - return - } - - l := r.Header().Get("Location") - if l == "" { - return - } - - u, _ := url.Parse(l) - if !strings.HasPrefix(u.Path, r.prefix) { - r.from = u.Path - u.Path = r.prefix + u.Path - r.Header().Set("Location", u.String()) - r.to = u.Path - r.tempBuffer = &bytes.Buffer{} - } - - statusCode = http.StatusFound -} diff --git a/pkg/schemaserver/urlbuilder/url.go b/pkg/schemaserver/urlbuilder/url.go deleted file mode 100644 index d552f9a..0000000 --- a/pkg/schemaserver/urlbuilder/url.go +++ /dev/null @@ -1,136 +0,0 @@ -package urlbuilder - -import ( - "net/http" - "net/url" - "path" - "strings" - - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/wrangler/pkg/name" -) - -const ( - PrefixHeader = "X-API-URL-Prefix" - ForwardedAPIHostHeader = "X-API-Host" - ForwardedHostHeader = "X-Forwarded-Host" - ForwardedProtoHeader = "X-Forwarded-Proto" - ForwardedPortHeader = "X-Forwarded-Port" -) - -func NewPrefixed(r *http.Request, schemas *types.APISchemas, prefix string) (types.URLBuilder, error) { - return New(r, &DefaultPathResolver{ - Prefix: prefix, - }, schemas) -} - -func New(r *http.Request, resolver PathResolver, schemas *types.APISchemas) (types.URLBuilder, error) { - requestURL := ParseRequestURL(r) - responseURLBase, err := ParseResponseURLBase(requestURL, r) - if err != nil { - return nil, err - } - - builder := &DefaultURLBuilder{ - schemas: schemas, - currentURL: requestURL, - responseURLBase: responseURLBase, - pathResolver: resolver, - query: r.URL.Query(), - } - - return builder, nil -} - -type PathResolver interface { - Schema(base string, schema *types.APISchema) string -} - -type DefaultPathResolver struct { - Prefix string -} - -func (d *DefaultPathResolver) Schema(base string, schema *types.APISchema) string { - return ConstructBasicURL(base, d.Prefix, schema.PluralName) -} - -type DefaultURLBuilder struct { - pathResolver PathResolver - schemas *types.APISchemas - currentURL string - responseURLBase string - query url.Values -} - -func (u *DefaultURLBuilder) Marker(marker string) string { - newValues := url.Values{} - for k, v := range u.query { - newValues[k] = v - } - newValues.Set("continue", marker) - return u.Current() + "?" + newValues.Encode() -} - -func (u *DefaultURLBuilder) Link(schema *types.APISchema, id string, linkName string) string { - if strings.Contains(id, "/") { - return u.schemaURL(schema, id, linkName) - } - return u.schemaURL(schema, id) + "?link=" + url.QueryEscape(linkName) -} - -func (u *DefaultURLBuilder) ResourceLink(schema *types.APISchema, id string) string { - return u.schemaURL(schema, id) -} - -func (u *DefaultURLBuilder) Current() string { - return u.currentURL -} - -func (u *DefaultURLBuilder) RelativeToRoot(path string) string { - if len(path) > 0 && path[0] != '/' { - return u.responseURLBase + "/" + path - } - return u.responseURLBase + path -} - -func (u *DefaultURLBuilder) Collection(schema *types.APISchema) string { - return u.schemaURL(schema) -} - -func (u *DefaultURLBuilder) schemaURL(schema *types.APISchema, parts ...string) string { - base := []string{ - u.pathResolver.Schema(u.responseURLBase, schema), - } - return ConstructBasicURL(append(base, parts...)...) -} - -func ConstructBasicURL(parts ...string) string { - switch len(parts) { - case 0: - return "" - case 1: - return parts[0] - default: - base := parts[0] - rest := path.Join(parts[1:]...) - if !strings.HasSuffix(base, "/") && !strings.HasPrefix(rest, "/") { - return base + "/" + rest - } - return base + rest - } -} - -func (u *DefaultURLBuilder) getPluralName(schema *types.APISchema) string { - if schema.PluralName == "" { - return strings.ToLower(name.GuessPluralName(schema.ID)) - } - return strings.ToLower(schema.PluralName) -} - -func (u *DefaultURLBuilder) Action(schema *types.APISchema, id, action string) string { - return u.schemaURL(schema, id) + "?action=" + url.QueryEscape(action) -} - -func (u *DefaultURLBuilder) CollectionAction(schema *types.APISchema, action string) string { - return u.schemaURL(schema) + "?action=" + url.QueryEscape(action) -} diff --git a/pkg/schemaserver/writer/encoding.go b/pkg/schemaserver/writer/encoding.go deleted file mode 100644 index 8115f2e..0000000 --- a/pkg/schemaserver/writer/encoding.go +++ /dev/null @@ -1,158 +0,0 @@ -package writer - -import ( - "io" - "net/http" - "strconv" - - "github.com/rancher/steve/pkg/schemaserver/types" -) - -type EncodingResponseWriter struct { - ContentType string - Encoder func(io.Writer, interface{}) error -} - -func (j *EncodingResponseWriter) start(apiOp *types.APIRequest, code int) { - AddCommonResponseHeader(apiOp) - apiOp.Response.Header().Set("content-type", j.ContentType) - apiOp.Response.WriteHeader(code) -} - -func (j *EncodingResponseWriter) Write(apiOp *types.APIRequest, code int, obj types.APIObject) { - j.start(apiOp, code) - j.Body(apiOp, apiOp.Response, obj) -} - -func (j *EncodingResponseWriter) WriteList(apiOp *types.APIRequest, code int, list types.APIObjectList) { - j.start(apiOp, code) - j.BodyList(apiOp, apiOp.Response, list) -} - -func (j *EncodingResponseWriter) Body(apiOp *types.APIRequest, writer io.Writer, obj types.APIObject) error { - return j.Encoder(writer, j.convert(apiOp, obj)) -} - -func (j *EncodingResponseWriter) BodyList(apiOp *types.APIRequest, writer io.Writer, list types.APIObjectList) error { - return j.Encoder(writer, j.convertList(apiOp, list)) -} - -func (j *EncodingResponseWriter) convertList(apiOp *types.APIRequest, input types.APIObjectList) *types.GenericCollection { - collection := newCollection(apiOp, input) - for _, value := range input.Objects { - converted := j.convert(apiOp, value) - collection.Data = append(collection.Data, converted) - } - - if apiOp.Schema.CollectionFormatter != nil { - apiOp.Schema.CollectionFormatter(apiOp, collection) - } - - if collection.Data == nil { - collection.Data = []*types.RawResource{} - } - - return collection -} - -func (j *EncodingResponseWriter) convert(context *types.APIRequest, input types.APIObject) *types.RawResource { - schema := context.Schemas.LookupSchema(input.Type) - if schema == nil { - schema = context.Schema - } - if schema == nil { - return nil - } - - rawResource := &types.RawResource{ - ID: input.ID, - Type: schema.ID, - Schema: schema, - Links: map[string]string{}, - Actions: map[string]string{}, - ActionLinks: context.Request.Header.Get("X-API-Action-Links") != "", - APIObject: input, - } - - j.addLinks(schema, context, input, rawResource) - - if schema.Formatter != nil { - schema.Formatter(context, rawResource) - } - - return rawResource -} - -func (j *EncodingResponseWriter) addLinks(schema *types.APISchema, context *types.APIRequest, input types.APIObject, rawResource *types.RawResource) { - if rawResource.ID == "" { - return - } - - self := context.URLBuilder.ResourceLink(rawResource.Schema, rawResource.ID) - if _, ok := rawResource.Links["self"]; !ok { - rawResource.Links["self"] = self - } - if _, ok := rawResource.Links["update"]; !ok { - if context.AccessControl.CanUpdate(context, input, schema) == nil { - rawResource.Links["update"] = self - } - } - if _, ok := rawResource.Links["remove"]; !ok { - if context.AccessControl.CanDelete(context, input, schema) == nil { - rawResource.Links["remove"] = self - } - } - for link := range schema.LinkHandlers { - rawResource.Links[link] = context.URLBuilder.Link(schema, rawResource.ID, link) - } - for action := range schema.ActionHandlers { - if rawResource.Actions == nil { - rawResource.Actions = map[string]string{} - } - rawResource.Actions[action] = context.URLBuilder.Action(schema, rawResource.ID, action) - } -} - -func getLimit(req *http.Request) int { - limit, err := strconv.Atoi(req.Header.Get("limit")) - if err == nil && limit > 0 { - return limit - } - return 0 -} - -func newCollection(apiOp *types.APIRequest, list types.APIObjectList) *types.GenericCollection { - result := &types.GenericCollection{ - Collection: types.Collection{ - Type: "collection", - ResourceType: apiOp.Type, - CreateTypes: map[string]string{}, - Links: map[string]string{ - "self": apiOp.URLBuilder.Current(), - }, - Actions: map[string]string{}, - Continue: list.Continue, - Revision: list.Revision, - }, - } - - partial := list.Continue != "" || apiOp.Query.Get("continue") != "" - if partial { - result.Pagination = &types.Pagination{ - Limit: getLimit(apiOp.Request), - First: apiOp.URLBuilder.Current(), - Partial: true, - } - if list.Continue != "" { - result.Pagination.Next = apiOp.URLBuilder.Marker(list.Continue) - } - } - - if apiOp.Method == http.MethodGet { - if apiOp.AccessControl.CanCreate(apiOp, apiOp.Schema) == nil { - result.CreateTypes[apiOp.Schema.ID] = apiOp.URLBuilder.Collection(apiOp.Schema) - } - } - - return result -} diff --git a/pkg/schemaserver/writer/gzip.go b/pkg/schemaserver/writer/gzip.go deleted file mode 100644 index c028fae..0000000 --- a/pkg/schemaserver/writer/gzip.go +++ /dev/null @@ -1,52 +0,0 @@ -package writer - -import ( - "compress/gzip" - "io" - "io/ioutil" - "net/http" - "strings" - - "github.com/rancher/steve/pkg/schemaserver/types" -) - -type GzipWriter struct { - types.ResponseWriter -} - -func setup(apiOp *types.APIRequest) (*types.APIRequest, io.Closer) { - if !strings.Contains(apiOp.Request.Header.Get("Accept-Encoding"), "gzip") { - return apiOp, ioutil.NopCloser(nil) - } - - apiOp.Response.Header().Set("Content-Encoding", "gzip") - apiOp.Response.Header().Del("Content-Length") - - gz := gzip.NewWriter(apiOp.Response) - gzw := &gzipResponseWriter{Writer: gz, ResponseWriter: apiOp.Response} - - newOp := *apiOp - newOp.Response = gzw - return &newOp, gz -} - -func (g *GzipWriter) Write(apiOp *types.APIRequest, code int, obj types.APIObject) { - apiOp, closer := setup(apiOp) - defer closer.Close() - g.ResponseWriter.Write(apiOp, code, obj) -} - -func (g *GzipWriter) WriteList(apiOp *types.APIRequest, code int, obj types.APIObjectList) { - apiOp, closer := setup(apiOp) - defer closer.Close() - g.ResponseWriter.WriteList(apiOp, code, obj) -} - -type gzipResponseWriter struct { - io.Writer - http.ResponseWriter -} - -func (g gzipResponseWriter) Write(b []byte) (int, error) { - return g.Writer.Write(b) -} diff --git a/pkg/schemaserver/writer/headers.go b/pkg/schemaserver/writer/headers.go deleted file mode 100644 index 314dd5e..0000000 --- a/pkg/schemaserver/writer/headers.go +++ /dev/null @@ -1,24 +0,0 @@ -package writer - -import ( - "github.com/rancher/steve/pkg/schemaserver/types" -) - -func AddCommonResponseHeader(apiOp *types.APIRequest) error { - addExpires(apiOp) - return addSchemasHeader(apiOp) -} - -func addSchemasHeader(apiOp *types.APIRequest) error { - schema := apiOp.Schemas.Schemas["schema"] - if schema == nil { - return nil - } - - apiOp.Response.Header().Set("X-Api-Schemas", apiOp.URLBuilder.Collection(schema)) - return nil -} - -func addExpires(apiOp *types.APIRequest) { - apiOp.Response.Header().Set("Expires", "Wed 24 Feb 1982 18:42:00 GMT") -} diff --git a/pkg/schemaserver/writer/html.go b/pkg/schemaserver/writer/html.go deleted file mode 100644 index 833f59c..0000000 --- a/pkg/schemaserver/writer/html.go +++ /dev/null @@ -1,85 +0,0 @@ -package writer - -import ( - "strings" - - "github.com/rancher/steve/pkg/schemaserver/types" -) - -const ( - JSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.js" - CSSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.css" - DefaultVersion = "1.1.8" -) - -var ( - start = ` - - - - - -`) -) - -type StringGetter func() string - -type HTMLResponseWriter struct { - EncodingResponseWriter - CSSURL StringGetter - JSURL StringGetter - APIUIVersion StringGetter -} - -func (h *HTMLResponseWriter) start(apiOp *types.APIRequest, code int) { - AddCommonResponseHeader(apiOp) - apiOp.Response.Header().Set("content-type", "text/html") - apiOp.Response.WriteHeader(code) -} - -func (h *HTMLResponseWriter) Write(apiOp *types.APIRequest, code int, obj types.APIObject) { - h.write(apiOp, code, obj) -} - -func (h *HTMLResponseWriter) WriteList(apiOp *types.APIRequest, code int, list types.APIObjectList) { - h.write(apiOp, code, list) -} - -func (h *HTMLResponseWriter) write(apiOp *types.APIRequest, code int, obj interface{}) { - h.start(apiOp, code) - schemaSchema := apiOp.Schemas.Schemas["schema"] - headerString := start - if schemaSchema != nil { - headerString = strings.Replace(headerString, "%SCHEMAS%", apiOp.URLBuilder.Collection(schemaSchema), 1) - } - var jsurl, cssurl string - if h.CSSURL != nil && h.JSURL != nil && h.CSSURL() != "" && h.JSURL() != "" { - jsurl = h.JSURL() - cssurl = h.CSSURL() - } else if h.APIUIVersion != nil && h.APIUIVersion() != "" { - jsurl = strings.Replace(JSURL, "%API_UI_VERSION%", h.APIUIVersion(), 1) - cssurl = strings.Replace(CSSURL, "%API_UI_VERSION%", h.APIUIVersion(), 1) - } else { - jsurl = strings.Replace(JSURL, "%API_UI_VERSION%", DefaultVersion, 1) - cssurl = strings.Replace(CSSURL, "%API_UI_VERSION%", DefaultVersion, 1) - } - headerString = strings.Replace(headerString, "%JSURL%", jsurl, 1) - headerString = strings.Replace(headerString, "%CSSURL%", cssurl, 1) - - apiOp.Response.Write([]byte(headerString)) - if apiObj, ok := obj.(types.APIObject); ok { - h.Body(apiOp, apiOp.Response, apiObj) - } else if list, ok := obj.(types.APIObjectList); ok { - h.BodyList(apiOp, apiOp.Response, list) - } - if schemaSchema != nil { - apiOp.Response.Write(end) - } -} diff --git a/pkg/server/config.go b/pkg/server/config.go index 6033f35..b50558c 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -5,11 +5,11 @@ import ( "net/http" "time" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/auth" "github.com/rancher/steve/pkg/client" "github.com/rancher/steve/pkg/schema" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/steve/pkg/server/router" "github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io" apiextensionsv1beta1 "github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io/v1beta1" diff --git a/pkg/server/handler/apiserver.go b/pkg/server/handler/apiserver.go index 5bb5f1c..2b5f07e 100644 --- a/pkg/server/handler/apiserver.go +++ b/pkg/server/handler/apiserver.go @@ -3,13 +3,13 @@ package handler import ( "net/http" + "github.com/rancher/apiserver/pkg/server" + "github.com/rancher/apiserver/pkg/types" + "github.com/rancher/apiserver/pkg/urlbuilder" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/auth" k8sproxy "github.com/rancher/steve/pkg/proxy" "github.com/rancher/steve/pkg/schema" - "github.com/rancher/steve/pkg/schemaserver/server" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/schemaserver/urlbuilder" "github.com/rancher/steve/pkg/server/router" "github.com/sirupsen/logrus" "k8s.io/apiserver/pkg/endpoints/request" diff --git a/pkg/server/handler/handlers.go b/pkg/server/handler/handlers.go index eb66bc5..0b4fa3e 100644 --- a/pkg/server/handler/handlers.go +++ b/pkg/server/handler/handlers.go @@ -2,9 +2,9 @@ package handler import ( "github.com/gorilla/mux" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/attributes" "github.com/rancher/steve/pkg/schema" - "github.com/rancher/steve/pkg/schemaserver/types" ) func k8sAPI(sf schema.Factory, apiOp *types.APIRequest) { diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index 86158bf..a9c2880 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -4,7 +4,7 @@ import ( "net/http" "github.com/gorilla/mux" - "github.com/rancher/steve/pkg/schemaserver/urlbuilder" + "github.com/rancher/apiserver/pkg/urlbuilder" ) type RouterFunc func(h Handlers) http.Handler diff --git a/pkg/server/server.go b/pkg/server/server.go index 2777897..bfb0e97 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -5,18 +5,18 @@ import ( "errors" "net/http" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/dynamiclistener/server" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/client" "github.com/rancher/steve/pkg/clustercache" schemacontroller "github.com/rancher/steve/pkg/controllers/schema" "github.com/rancher/steve/pkg/dashboard" + "github.com/rancher/steve/pkg/resources" + "github.com/rancher/steve/pkg/resources/common" + "github.com/rancher/steve/pkg/resources/schemas" "github.com/rancher/steve/pkg/schema" - "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/steve/pkg/server/handler" - "github.com/rancher/steve/pkg/server/resources" - "github.com/rancher/steve/pkg/server/resources/common" - "github.com/rancher/steve/pkg/server/resources/schemas" ) var ErrConfigRequired = errors.New("rest config is required") diff --git a/pkg/server/store/partition/parallel.go b/pkg/stores/partition/parallel.go similarity index 98% rename from pkg/server/store/partition/parallel.go rename to pkg/stores/partition/parallel.go index bbf1ea5..e1901d3 100644 --- a/pkg/server/store/partition/parallel.go +++ b/pkg/stores/partition/parallel.go @@ -5,7 +5,7 @@ import ( "encoding/base64" "encoding/json" - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/types" "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" ) diff --git a/pkg/server/store/partition/store.go b/pkg/stores/partition/store.go similarity index 98% rename from pkg/server/store/partition/store.go rename to pkg/stores/partition/store.go index cac1113..3d5fb57 100644 --- a/pkg/server/store/partition/store.go +++ b/pkg/stores/partition/store.go @@ -5,7 +5,7 @@ import ( "net/http" "strconv" - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/types" "golang.org/x/sync/errgroup" ) diff --git a/pkg/server/store/proxy/error_wrapper.go b/pkg/stores/proxy/error_wrapper.go similarity index 91% rename from pkg/server/store/proxy/error_wrapper.go rename to pkg/stores/proxy/error_wrapper.go index 43c2ef1..4aa9fa4 100644 --- a/pkg/server/store/proxy/error_wrapper.go +++ b/pkg/stores/proxy/error_wrapper.go @@ -1,8 +1,8 @@ package proxy import ( - "github.com/rancher/steve/pkg/schemaserver/httperror" - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/apierror" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/wrangler/pkg/schemas/validation" "k8s.io/apimachinery/pkg/api/errors" ) @@ -47,7 +47,7 @@ func (e *errorStore) Watch(apiOp *types.APIRequest, schema *types.APISchema, wr func translateError(err error) error { if apiError, ok := err.(errors.APIStatus); ok { status := apiError.Status() - return httperror.NewAPIError(validation.ErrorCode{ + return apierror.NewAPIError(validation.ErrorCode{ Status: int(status.Code), Code: string(status.Reason), }, status.Message) diff --git a/pkg/server/store/proxy/proxy_store.go b/pkg/stores/proxy/proxy_store.go similarity index 99% rename from pkg/server/store/proxy/proxy_store.go rename to pkg/stores/proxy/proxy_store.go index 02e188c..babbf43 100644 --- a/pkg/server/store/proxy/proxy_store.go +++ b/pkg/stores/proxy/proxy_store.go @@ -10,10 +10,10 @@ import ( "regexp" "github.com/pkg/errors" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/partition" + "github.com/rancher/steve/pkg/stores/partition" "github.com/rancher/wrangler/pkg/data" "github.com/rancher/wrangler/pkg/schemas/validation" "github.com/sirupsen/logrus" diff --git a/pkg/server/store/proxy/rbac_store.go b/pkg/stores/proxy/rbac_store.go similarity index 97% rename from pkg/server/store/proxy/rbac_store.go rename to pkg/stores/proxy/rbac_store.go index 4878070..dad1a87 100644 --- a/pkg/server/store/proxy/rbac_store.go +++ b/pkg/stores/proxy/rbac_store.go @@ -6,10 +6,10 @@ import ( "net/http" "sort" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/attributes" - "github.com/rancher/steve/pkg/schemaserver/types" - "github.com/rancher/steve/pkg/server/store/partition" + "github.com/rancher/steve/pkg/stores/partition" "k8s.io/apimachinery/pkg/util/sets" ) diff --git a/pkg/server/store/proxy/watch_refresh.go b/pkg/stores/proxy/watch_refresh.go similarity index 94% rename from pkg/server/store/proxy/watch_refresh.go rename to pkg/stores/proxy/watch_refresh.go index 47fe94d..87ecda0 100644 --- a/pkg/server/store/proxy/watch_refresh.go +++ b/pkg/stores/proxy/watch_refresh.go @@ -4,8 +4,8 @@ import ( "context" "time" + "github.com/rancher/apiserver/pkg/types" "github.com/rancher/steve/pkg/accesscontrol" - "github.com/rancher/steve/pkg/schemaserver/types" "k8s.io/apiserver/pkg/endpoints/request" ) diff --git a/pkg/server/store/selector/selector.go b/pkg/stores/selector/selector.go similarity index 93% rename from pkg/server/store/selector/selector.go rename to pkg/stores/selector/selector.go index 13ffd99..7298244 100644 --- a/pkg/server/store/selector/selector.go +++ b/pkg/stores/selector/selector.go @@ -1,7 +1,7 @@ package selector import ( - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/types" "k8s.io/apimachinery/pkg/labels" ) diff --git a/pkg/server/store/switchschema/store.go b/pkg/stores/switchschema/store.go similarity index 97% rename from pkg/server/store/switchschema/store.go rename to pkg/stores/switchschema/store.go index f935f35..0d15731 100644 --- a/pkg/server/store/switchschema/store.go +++ b/pkg/stores/switchschema/store.go @@ -1,7 +1,7 @@ package switchschema import ( - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/types" ) type Store struct { diff --git a/pkg/server/store/switchstore/store.go b/pkg/stores/switchstore/store.go similarity index 97% rename from pkg/server/store/switchstore/store.go rename to pkg/stores/switchstore/store.go index ed527c8..95dfb31 100644 --- a/pkg/server/store/switchstore/store.go +++ b/pkg/stores/switchstore/store.go @@ -1,7 +1,7 @@ package switchstore import ( - "github.com/rancher/steve/pkg/schemaserver/types" + "github.com/rancher/apiserver/pkg/types" ) type StorePicker func(apiOp *types.APIRequest, schema *types.APISchema, verb, id string) (types.Store, error)