14287: Fix debug log level.
[arvados.git] / lib / controller / router / router.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package router
6
7 import (
8         "context"
9         "net/http"
10
11         "git.curoverse.com/arvados.git/lib/controller/federation"
12         "git.curoverse.com/arvados.git/sdk/go/arvados"
13         "git.curoverse.com/arvados.git/sdk/go/auth"
14         "git.curoverse.com/arvados.git/sdk/go/ctxlog"
15         "github.com/julienschmidt/httprouter"
16 )
17
18 type router struct {
19         mux *httprouter.Router
20         fed federation.Interface
21 }
22
23 func New(cluster *arvados.Cluster) *router {
24         rtr := &router{
25                 mux: httprouter.New(),
26                 fed: federation.New(cluster),
27         }
28         rtr.addRoutes(cluster)
29         return rtr
30 }
31
32 func (rtr *router) addRoutes(cluster *arvados.Cluster) {
33         for _, route := range []struct {
34                 endpoint    arvados.APIEndpoint
35                 defaultOpts func() interface{}
36                 exec        func(ctx context.Context, opts interface{}) (interface{}, error)
37         }{
38                 {
39                         arvados.EndpointCollectionCreate,
40                         func() interface{} { return &arvados.CreateOptions{} },
41                         func(ctx context.Context, opts interface{}) (interface{}, error) {
42                                 return rtr.fed.CollectionCreate(ctx, *opts.(*arvados.CreateOptions))
43                         },
44                 },
45                 {
46                         arvados.EndpointCollectionUpdate,
47                         func() interface{} { return &arvados.UpdateOptions{} },
48                         func(ctx context.Context, opts interface{}) (interface{}, error) {
49                                 return rtr.fed.CollectionUpdate(ctx, *opts.(*arvados.UpdateOptions))
50                         },
51                 },
52                 {
53                         arvados.EndpointCollectionGet,
54                         func() interface{} { return &arvados.GetOptions{} },
55                         func(ctx context.Context, opts interface{}) (interface{}, error) {
56                                 return rtr.fed.CollectionGet(ctx, *opts.(*arvados.GetOptions))
57                         },
58                 },
59                 {
60                         arvados.EndpointCollectionList,
61                         func() interface{} { return &arvados.ListOptions{Limit: -1} },
62                         func(ctx context.Context, opts interface{}) (interface{}, error) {
63                                 return rtr.fed.CollectionList(ctx, *opts.(*arvados.ListOptions))
64                         },
65                 },
66                 {
67                         arvados.EndpointCollectionDelete,
68                         func() interface{} { return &arvados.DeleteOptions{} },
69                         func(ctx context.Context, opts interface{}) (interface{}, error) {
70                                 return rtr.fed.CollectionDelete(ctx, *opts.(*arvados.DeleteOptions))
71                         },
72                 },
73                 {
74                         arvados.EndpointContainerCreate,
75                         func() interface{} { return &arvados.CreateOptions{} },
76                         func(ctx context.Context, opts interface{}) (interface{}, error) {
77                                 return rtr.fed.ContainerCreate(ctx, *opts.(*arvados.CreateOptions))
78                         },
79                 },
80                 {
81                         arvados.EndpointContainerUpdate,
82                         func() interface{} { return &arvados.UpdateOptions{} },
83                         func(ctx context.Context, opts interface{}) (interface{}, error) {
84                                 return rtr.fed.ContainerUpdate(ctx, *opts.(*arvados.UpdateOptions))
85                         },
86                 },
87                 {
88                         arvados.EndpointContainerGet,
89                         func() interface{} { return &arvados.GetOptions{} },
90                         func(ctx context.Context, opts interface{}) (interface{}, error) {
91                                 return rtr.fed.ContainerGet(ctx, *opts.(*arvados.GetOptions))
92                         },
93                 },
94                 {
95                         arvados.EndpointContainerList,
96                         func() interface{} { return &arvados.ListOptions{Limit: -1} },
97                         func(ctx context.Context, opts interface{}) (interface{}, error) {
98                                 return rtr.fed.ContainerList(ctx, *opts.(*arvados.ListOptions))
99                         },
100                 },
101                 {
102                         arvados.EndpointContainerDelete,
103                         func() interface{} { return &arvados.DeleteOptions{} },
104                         func(ctx context.Context, opts interface{}) (interface{}, error) {
105                                 return rtr.fed.ContainerDelete(ctx, *opts.(*arvados.DeleteOptions))
106                         },
107                 },
108                 {
109                         arvados.EndpointContainerLock,
110                         func() interface{} {
111                                 return &arvados.GetOptions{Select: []string{"uuid", "state", "priority", "auth_uuid", "locked_by_uuid"}}
112                         },
113                         func(ctx context.Context, opts interface{}) (interface{}, error) {
114                                 return rtr.fed.ContainerLock(ctx, *opts.(*arvados.GetOptions))
115                         },
116                 },
117                 {
118                         arvados.EndpointContainerUnlock,
119                         func() interface{} {
120                                 return &arvados.GetOptions{Select: []string{"uuid", "state", "priority", "auth_uuid", "locked_by_uuid"}}
121                         },
122                         func(ctx context.Context, opts interface{}) (interface{}, error) {
123                                 return rtr.fed.ContainerUnlock(ctx, *opts.(*arvados.GetOptions))
124                         },
125                 },
126                 {
127                         arvados.EndpointSpecimenCreate,
128                         func() interface{} { return &arvados.CreateOptions{} },
129                         func(ctx context.Context, opts interface{}) (interface{}, error) {
130                                 return rtr.fed.SpecimenCreate(ctx, *opts.(*arvados.CreateOptions))
131                         },
132                 },
133                 {
134                         arvados.EndpointSpecimenUpdate,
135                         func() interface{} { return &arvados.UpdateOptions{} },
136                         func(ctx context.Context, opts interface{}) (interface{}, error) {
137                                 return rtr.fed.SpecimenUpdate(ctx, *opts.(*arvados.UpdateOptions))
138                         },
139                 },
140                 {
141                         arvados.EndpointSpecimenGet,
142                         func() interface{} { return &arvados.GetOptions{} },
143                         func(ctx context.Context, opts interface{}) (interface{}, error) {
144                                 return rtr.fed.SpecimenGet(ctx, *opts.(*arvados.GetOptions))
145                         },
146                 },
147                 {
148                         arvados.EndpointSpecimenList,
149                         func() interface{} { return &arvados.ListOptions{Limit: -1} },
150                         func(ctx context.Context, opts interface{}) (interface{}, error) {
151                                 return rtr.fed.SpecimenList(ctx, *opts.(*arvados.ListOptions))
152                         },
153                 },
154                 {
155                         arvados.EndpointSpecimenDelete,
156                         func() interface{} { return &arvados.DeleteOptions{} },
157                         func(ctx context.Context, opts interface{}) (interface{}, error) {
158                                 return rtr.fed.SpecimenDelete(ctx, *opts.(*arvados.DeleteOptions))
159                         },
160                 },
161         } {
162                 route := route
163                 methods := []string{route.endpoint.Method}
164                 if route.endpoint.Method == "PATCH" {
165                         methods = append(methods, "PUT")
166                 }
167                 for _, method := range methods {
168                         rtr.mux.HandlerFunc(method, "/"+route.endpoint.Path, func(w http.ResponseWriter, req *http.Request) {
169                                 params, err := rtr.loadRequestParams(req, route.endpoint.AttrsKey)
170                                 if err != nil {
171                                         rtr.sendError(w, err)
172                                         return
173                                 }
174                                 opts := route.defaultOpts()
175                                 err = rtr.transcode(params, opts)
176                                 if err != nil {
177                                         rtr.sendError(w, err)
178                                         return
179                                 }
180                                 respOpts, err := rtr.responseOptions(opts)
181                                 if err != nil {
182                                         rtr.sendError(w, err)
183                                         return
184                                 }
185
186                                 creds := auth.CredentialsFromRequest(req)
187                                 ctx := req.Context()
188                                 ctx = context.WithValue(ctx, auth.ContextKeyCredentials, creds)
189                                 ctx = arvados.ContextWithRequestID(ctx, req.Header.Get("X-Request-Id"))
190                                 resp, err := route.exec(ctx, opts)
191                                 if err != nil {
192                                         ctxlog.FromContext(ctx).WithError(err).Debugf("returning error response for %#v", err)
193                                         rtr.sendError(w, err)
194                                         return
195                                 }
196                                 rtr.sendResponse(w, resp, respOpts)
197                         })
198                 }
199         }
200 }
201
202 func (rtr *router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
203         r.ParseForm()
204         if m := r.FormValue("_method"); m != "" {
205                 r2 := *r
206                 r = &r2
207                 r.Method = m
208         }
209         rtr.mux.ServeHTTP(w, r)
210 }