"net/http"
"strings"
- "git.curoverse.com/arvados.git/lib/controller/federation"
"git.curoverse.com/arvados.git/sdk/go/arvados"
"git.curoverse.com/arvados.git/sdk/go/auth"
"git.curoverse.com/arvados.git/sdk/go/ctxlog"
type router struct {
mux *httprouter.Router
- fed federation.Interface
+ fed arvados.API
}
-func New(cluster *arvados.Cluster) *router {
+func New(fed arvados.API) *router {
rtr := &router{
mux: httprouter.New(),
- fed: federation.New(cluster),
+ fed: fed,
}
- rtr.addRoutes(cluster)
+ rtr.addRoutes()
return rtr
}
-func (rtr *router) addRoutes(cluster *arvados.Cluster) {
+type routableFunc func(ctx context.Context, opts interface{}) (interface{}, error)
+
+func (rtr *router) addRoutes() {
for _, route := range []struct {
endpoint arvados.APIEndpoint
defaultOpts func() interface{}
- exec func(ctx context.Context, opts interface{}) (interface{}, error)
+ exec routableFunc
}{
+ {
+ arvados.EndpointConfigGet,
+ func() interface{} { return &struct{}{} },
+ func(ctx context.Context, opts interface{}) (interface{}, error) {
+ return rtr.fed.ConfigGet(ctx)
+ },
+ },
+ {
+ arvados.EndpointLogin,
+ func() interface{} { return &arvados.LoginOptions{} },
+ func(ctx context.Context, opts interface{}) (interface{}, error) {
+ return rtr.fed.Login(ctx, *opts.(*arvados.LoginOptions))
+ },
+ },
{
arvados.EndpointCollectionCreate,
func() interface{} { return &arvados.CreateOptions{} },
},
{
arvados.EndpointCollectionTrash,
- func() interface{} { return &arvados.GetOptions{} },
+ func() interface{} { return &arvados.DeleteOptions{} },
func(ctx context.Context, opts interface{}) (interface{}, error) {
- return rtr.fed.CollectionTrash(ctx, *opts.(*arvados.GetOptions))
+ return rtr.fed.CollectionTrash(ctx, *opts.(*arvados.DeleteOptions))
},
},
{
arvados.EndpointCollectionUntrash,
- func() interface{} { return &arvados.GetOptions{} },
+ func() interface{} { return &arvados.UntrashOptions{} },
func(ctx context.Context, opts interface{}) (interface{}, error) {
- return rtr.fed.CollectionUntrash(ctx, *opts.(*arvados.GetOptions))
+ return rtr.fed.CollectionUntrash(ctx, *opts.(*arvados.UntrashOptions))
},
},
{
},
},
} {
- route := route
- methods := []string{route.endpoint.Method}
+ rtr.addRoute(route.endpoint, route.defaultOpts, route.exec)
if route.endpoint.Method == "PATCH" {
- methods = append(methods, "PUT")
- }
- for _, method := range methods {
- rtr.mux.HandlerFunc(method, "/"+route.endpoint.Path, func(w http.ResponseWriter, req *http.Request) {
- logger := ctxlog.FromContext(req.Context())
- params, err := rtr.loadRequestParams(req, route.endpoint.AttrsKey)
- if err != nil {
- logger.WithField("req", req).WithField("route", route).WithError(err).Debug("error loading request params")
- rtr.sendError(w, err)
- return
- }
- opts := route.defaultOpts()
- err = rtr.transcode(params, opts)
- if err != nil {
- logger.WithField("params", params).WithError(err).Debugf("error transcoding params to %T", opts)
- rtr.sendError(w, err)
- return
- }
- respOpts, err := rtr.responseOptions(opts)
- if err != nil {
- logger.WithField("opts", opts).WithError(err).Debugf("error getting response options from %T", opts)
- rtr.sendError(w, err)
- return
- }
-
- creds := auth.CredentialsFromRequest(req)
- if rt, _ := params["reader_tokens"].([]interface{}); len(rt) > 0 {
- for _, t := range rt {
- if t, ok := t.(string); ok {
- creds.Tokens = append(creds.Tokens, t)
- }
- }
- }
- ctx := req.Context()
- ctx = context.WithValue(ctx, auth.ContextKeyCredentials, creds)
- ctx = arvados.ContextWithRequestID(ctx, req.Header.Get("X-Request-Id"))
- logger.WithFields(logrus.Fields{
- "apiEndpoint": route.endpoint,
- "apiOptsType": fmt.Sprintf("%T", opts),
- "apiOpts": opts,
- }).Debug("exec")
- resp, err := route.exec(ctx, opts)
- if err != nil {
- logger.WithError(err).Debugf("returning error type %T", err)
- rtr.sendError(w, err)
- return
- }
- rtr.sendResponse(w, resp, respOpts)
- })
+ // Accept PUT as a synonym for PATCH.
+ endpointPUT := route.endpoint
+ endpointPUT.Method = "PUT"
+ rtr.addRoute(endpointPUT, route.defaultOpts, route.exec)
}
}
rtr.mux.NotFound = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
httpserver.Errors(w, []string{"API endpoint not found"}, http.StatusNotFound)
})
+ rtr.mux.MethodNotAllowed = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ httpserver.Errors(w, []string{"API endpoint not found"}, http.StatusMethodNotAllowed)
+ })
+}
+
+func (rtr *router) addRoute(endpoint arvados.APIEndpoint, defaultOpts func() interface{}, exec routableFunc) {
+ rtr.mux.HandlerFunc(endpoint.Method, "/"+endpoint.Path, func(w http.ResponseWriter, req *http.Request) {
+ logger := ctxlog.FromContext(req.Context())
+ params, err := rtr.loadRequestParams(req, endpoint.AttrsKey)
+ if err != nil {
+ logger.WithFields(logrus.Fields{
+ "req": req,
+ "method": endpoint.Method,
+ "endpoint": endpoint,
+ }).WithError(err).Debug("error loading request params")
+ rtr.sendError(w, err)
+ return
+ }
+ opts := defaultOpts()
+ err = rtr.transcode(params, opts)
+ if err != nil {
+ logger.WithField("params", params).WithError(err).Debugf("error transcoding params to %T", opts)
+ rtr.sendError(w, err)
+ return
+ }
+ respOpts, err := rtr.responseOptions(opts)
+ if err != nil {
+ logger.WithField("opts", opts).WithError(err).Debugf("error getting response options from %T", opts)
+ rtr.sendError(w, err)
+ return
+ }
+
+ creds := auth.CredentialsFromRequest(req)
+ if rt, _ := params["reader_tokens"].([]interface{}); len(rt) > 0 {
+ for _, t := range rt {
+ if t, ok := t.(string); ok {
+ creds.Tokens = append(creds.Tokens, t)
+ }
+ }
+ }
+ ctx := auth.NewContext(req.Context(), creds)
+ ctx = arvados.ContextWithRequestID(ctx, req.Header.Get("X-Request-Id"))
+ logger.WithFields(logrus.Fields{
+ "apiEndpoint": endpoint,
+ "apiOptsType": fmt.Sprintf("%T", opts),
+ "apiOpts": opts,
+ }).Debug("exec")
+ resp, err := exec(ctx, opts)
+ if err != nil {
+ logger.WithError(err).Debugf("returning error type %T", err)
+ rtr.sendError(w, err)
+ return
+ }
+ rtr.sendResponse(w, req, resp, respOpts)
+ })
}
func (rtr *router) ServeHTTP(w http.ResponseWriter, r *http.Request) {