1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
12 "git.curoverse.com/arvados.git/lib/controller/federation"
13 "git.curoverse.com/arvados.git/sdk/go/arvados"
14 "git.curoverse.com/arvados.git/sdk/go/auth"
15 "git.curoverse.com/arvados.git/sdk/go/ctxlog"
16 "github.com/julienschmidt/httprouter"
20 mux *httprouter.Router
21 fed federation.Interface
24 func New(cluster *arvados.Cluster) *router {
26 mux: httprouter.New(),
27 fed: federation.New(cluster),
29 rtr.addRoutes(cluster)
33 func (rtr *router) addRoutes(cluster *arvados.Cluster) {
34 for _, route := range []struct {
35 endpoint arvados.APIEndpoint
36 defaultOpts func() interface{}
37 exec func(ctx context.Context, opts interface{}) (interface{}, error)
40 arvados.EndpointCollectionCreate,
41 func() interface{} { return &arvados.CreateOptions{} },
42 func(ctx context.Context, opts interface{}) (interface{}, error) {
43 return rtr.fed.CollectionCreate(ctx, *opts.(*arvados.CreateOptions))
47 arvados.EndpointCollectionUpdate,
48 func() interface{} { return &arvados.UpdateOptions{} },
49 func(ctx context.Context, opts interface{}) (interface{}, error) {
50 return rtr.fed.CollectionUpdate(ctx, *opts.(*arvados.UpdateOptions))
54 arvados.EndpointCollectionGet,
55 func() interface{} { return &arvados.GetOptions{} },
56 func(ctx context.Context, opts interface{}) (interface{}, error) {
57 return rtr.fed.CollectionGet(ctx, *opts.(*arvados.GetOptions))
61 arvados.EndpointCollectionList,
62 func() interface{} { return &arvados.ListOptions{Limit: -1} },
63 func(ctx context.Context, opts interface{}) (interface{}, error) {
64 return rtr.fed.CollectionList(ctx, *opts.(*arvados.ListOptions))
68 arvados.EndpointCollectionProvenance,
69 func() interface{} { return &arvados.GetOptions{} },
70 func(ctx context.Context, opts interface{}) (interface{}, error) {
71 return rtr.fed.CollectionProvenance(ctx, *opts.(*arvados.GetOptions))
75 arvados.EndpointCollectionUsedBy,
76 func() interface{} { return &arvados.GetOptions{} },
77 func(ctx context.Context, opts interface{}) (interface{}, error) {
78 return rtr.fed.CollectionUsedBy(ctx, *opts.(*arvados.GetOptions))
82 arvados.EndpointCollectionDelete,
83 func() interface{} { return &arvados.DeleteOptions{} },
84 func(ctx context.Context, opts interface{}) (interface{}, error) {
85 return rtr.fed.CollectionDelete(ctx, *opts.(*arvados.DeleteOptions))
89 arvados.EndpointContainerCreate,
90 func() interface{} { return &arvados.CreateOptions{} },
91 func(ctx context.Context, opts interface{}) (interface{}, error) {
92 return rtr.fed.ContainerCreate(ctx, *opts.(*arvados.CreateOptions))
96 arvados.EndpointContainerUpdate,
97 func() interface{} { return &arvados.UpdateOptions{} },
98 func(ctx context.Context, opts interface{}) (interface{}, error) {
99 return rtr.fed.ContainerUpdate(ctx, *opts.(*arvados.UpdateOptions))
103 arvados.EndpointContainerGet,
104 func() interface{} { return &arvados.GetOptions{} },
105 func(ctx context.Context, opts interface{}) (interface{}, error) {
106 return rtr.fed.ContainerGet(ctx, *opts.(*arvados.GetOptions))
110 arvados.EndpointContainerList,
111 func() interface{} { return &arvados.ListOptions{Limit: -1} },
112 func(ctx context.Context, opts interface{}) (interface{}, error) {
113 return rtr.fed.ContainerList(ctx, *opts.(*arvados.ListOptions))
117 arvados.EndpointContainerDelete,
118 func() interface{} { return &arvados.DeleteOptions{} },
119 func(ctx context.Context, opts interface{}) (interface{}, error) {
120 return rtr.fed.ContainerDelete(ctx, *opts.(*arvados.DeleteOptions))
124 arvados.EndpointContainerLock,
126 return &arvados.GetOptions{Select: []string{"uuid", "state", "priority", "auth_uuid", "locked_by_uuid"}}
128 func(ctx context.Context, opts interface{}) (interface{}, error) {
129 return rtr.fed.ContainerLock(ctx, *opts.(*arvados.GetOptions))
133 arvados.EndpointContainerUnlock,
135 return &arvados.GetOptions{Select: []string{"uuid", "state", "priority", "auth_uuid", "locked_by_uuid"}}
137 func(ctx context.Context, opts interface{}) (interface{}, error) {
138 return rtr.fed.ContainerUnlock(ctx, *opts.(*arvados.GetOptions))
142 arvados.EndpointSpecimenCreate,
143 func() interface{} { return &arvados.CreateOptions{} },
144 func(ctx context.Context, opts interface{}) (interface{}, error) {
145 return rtr.fed.SpecimenCreate(ctx, *opts.(*arvados.CreateOptions))
149 arvados.EndpointSpecimenUpdate,
150 func() interface{} { return &arvados.UpdateOptions{} },
151 func(ctx context.Context, opts interface{}) (interface{}, error) {
152 return rtr.fed.SpecimenUpdate(ctx, *opts.(*arvados.UpdateOptions))
156 arvados.EndpointSpecimenGet,
157 func() interface{} { return &arvados.GetOptions{} },
158 func(ctx context.Context, opts interface{}) (interface{}, error) {
159 return rtr.fed.SpecimenGet(ctx, *opts.(*arvados.GetOptions))
163 arvados.EndpointSpecimenList,
164 func() interface{} { return &arvados.ListOptions{Limit: -1} },
165 func(ctx context.Context, opts interface{}) (interface{}, error) {
166 return rtr.fed.SpecimenList(ctx, *opts.(*arvados.ListOptions))
170 arvados.EndpointSpecimenDelete,
171 func() interface{} { return &arvados.DeleteOptions{} },
172 func(ctx context.Context, opts interface{}) (interface{}, error) {
173 return rtr.fed.SpecimenDelete(ctx, *opts.(*arvados.DeleteOptions))
178 methods := []string{route.endpoint.Method}
179 if route.endpoint.Method == "PATCH" {
180 methods = append(methods, "PUT")
182 for _, method := range methods {
183 rtr.mux.HandlerFunc(method, "/"+route.endpoint.Path, func(w http.ResponseWriter, req *http.Request) {
184 params, err := rtr.loadRequestParams(req, route.endpoint.AttrsKey)
186 rtr.sendError(w, err)
189 opts := route.defaultOpts()
190 err = rtr.transcode(params, opts)
192 rtr.sendError(w, err)
195 respOpts, err := rtr.responseOptions(opts)
197 rtr.sendError(w, err)
201 creds := auth.CredentialsFromRequest(req)
202 if rt, _ := params["reader_tokens"].([]interface{}); len(rt) > 0 {
203 for _, t := range rt {
204 if t, ok := t.(string); ok {
205 creds.Tokens = append(creds.Tokens, t)
210 ctx = context.WithValue(ctx, auth.ContextKeyCredentials, creds)
211 ctx = arvados.ContextWithRequestID(ctx, req.Header.Get("X-Request-Id"))
212 resp, err := route.exec(ctx, opts)
214 ctxlog.FromContext(ctx).WithError(err).Debugf("returning error response for %#v", err)
215 rtr.sendError(w, err)
218 rtr.sendResponse(w, resp, respOpts)
224 func (rtr *router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
225 switch strings.SplitN(strings.TrimLeft(r.URL.Path, "/"), "/", 2)[0] {
226 case "login", "logout", "auth":
228 w.Header().Set("Access-Control-Allow-Origin", "*")
229 w.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, PUT, POST, DELETE")
230 w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
231 w.Header().Set("Access-Control-Max-Age", "86486400")
233 if r.Method == "OPTIONS" {
237 if m := r.FormValue("_method"); m != "" {
242 rtr.mux.ServeHTTP(w, r)