1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
20 "git.arvados.org/arvados.git/sdk/go/arvados"
21 "git.arvados.org/arvados.git/sdk/go/auth"
24 type TokenProvider func(context.Context) ([]string, error)
26 func PassthroughTokenProvider(ctx context.Context) ([]string, error) {
27 if incoming, ok := auth.FromContext(ctx); !ok {
28 return nil, errors.New("no token provided")
30 return incoming.Tokens, nil
35 SendHeader http.Header
37 httpClient http.Client
39 tokenProvider TokenProvider
42 func NewConn(clusterID string, url *url.URL, insecure bool, tp TokenProvider) *Conn {
43 transport := http.DefaultTransport
45 // It's not safe to copy *http.DefaultTransport
46 // because it has a mutex (which might be locked)
47 // protecting a private map (which might not be nil).
48 // So we build our own, using the Go 1.12 default
49 // values, ignoring any changes the application has
50 // made to http.DefaultTransport.
51 transport = &http.Transport{
52 DialContext: (&net.Dialer{
53 Timeout: 30 * time.Second,
54 KeepAlive: 30 * time.Second,
58 IdleConnTimeout: 90 * time.Second,
59 TLSHandshakeTimeout: 10 * time.Second,
60 ExpectContinueTimeout: 1 * time.Second,
61 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
66 httpClient: http.Client{
67 CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
75 func (conn *Conn) requestAndDecode(ctx context.Context, dst interface{}, ep arvados.APIEndpoint, body io.Reader, opts interface{}) error {
76 aClient := arvados.Client{
77 Client: &conn.httpClient,
78 Scheme: conn.baseURL.Scheme,
79 APIHost: conn.baseURL.Host,
80 SendHeader: conn.SendHeader,
82 tokens, err := conn.tokenProvider(ctx)
85 } else if len(tokens) > 0 {
86 ctx = arvados.ContextWithAuthorization(ctx, "Bearer "+tokens[0])
88 // Use a non-empty auth string to ensure we override
89 // any default token set on aClient -- and to avoid
90 // having the remote prompt us to send a token by
92 ctx = arvados.ContextWithAuthorization(ctx, "Bearer -")
95 // Encode opts to JSON and decode from there to a
96 // map[string]interface{}, so we can munge the query params
97 // using the JSON key names specified by opts' struct tags.
98 j, err := json.Marshal(opts)
100 return fmt.Errorf("%T: requestAndDecode: Marshal opts: %s", conn, err)
102 var params map[string]interface{}
103 err = json.Unmarshal(j, ¶ms)
105 return fmt.Errorf("%T: requestAndDecode: Unmarshal opts: %s", conn, err)
107 if attrs, ok := params["attrs"]; ok && ep.AttrsKey != "" {
108 params[ep.AttrsKey] = attrs
109 delete(params, "attrs")
111 if limit, ok := params["limit"].(float64); ok && limit < 0 {
112 // Negative limit means "not specified" here, but some
113 // servers/versions do not accept that, so we need to
114 // remove it entirely.
115 delete(params, "limit")
118 params["reader_tokens"] = tokens[1:]
121 if strings.Contains(ep.Path, "/{uuid}") {
122 uuid, _ := params["uuid"].(string)
123 path = strings.Replace(path, "/{uuid}", "/"+uuid, 1)
124 delete(params, "uuid")
126 return aClient.RequestAndDecodeContext(ctx, dst, ep.Method, path, body, params)
129 func (conn *Conn) BaseURL() url.URL {
133 func (conn *Conn) ConfigGet(ctx context.Context) (json.RawMessage, error) {
134 ep := arvados.EndpointConfigGet
135 var resp json.RawMessage
136 err := conn.requestAndDecode(ctx, &resp, ep, nil, nil)
140 func (conn *Conn) Login(ctx context.Context, options arvados.LoginOptions) (arvados.LoginResponse, error) {
141 ep := arvados.EndpointLogin
142 var resp arvados.LoginResponse
143 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
144 resp.RedirectLocation = conn.relativeToBaseURL(resp.RedirectLocation)
148 func (conn *Conn) Logout(ctx context.Context, options arvados.LogoutOptions) (arvados.LogoutResponse, error) {
149 ep := arvados.EndpointLogout
150 var resp arvados.LogoutResponse
151 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
152 resp.RedirectLocation = conn.relativeToBaseURL(resp.RedirectLocation)
156 // If the given location is a valid URL and its origin is the same as
157 // conn.baseURL, return it as a relative URL. Otherwise, return it
159 func (conn *Conn) relativeToBaseURL(location string) string {
160 u, err := url.Parse(location)
161 if err == nil && u.Scheme == conn.baseURL.Scheme && strings.ToLower(u.Host) == strings.ToLower(conn.baseURL.Host) {
172 func (conn *Conn) CollectionCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Collection, error) {
173 ep := arvados.EndpointCollectionCreate
174 var resp arvados.Collection
175 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
179 func (conn *Conn) CollectionUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.Collection, error) {
180 ep := arvados.EndpointCollectionUpdate
181 var resp arvados.Collection
182 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
186 func (conn *Conn) CollectionGet(ctx context.Context, options arvados.GetOptions) (arvados.Collection, error) {
187 ep := arvados.EndpointCollectionGet
188 var resp arvados.Collection
189 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
193 func (conn *Conn) CollectionList(ctx context.Context, options arvados.ListOptions) (arvados.CollectionList, error) {
194 ep := arvados.EndpointCollectionList
195 var resp arvados.CollectionList
196 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
200 func (conn *Conn) CollectionProvenance(ctx context.Context, options arvados.GetOptions) (map[string]interface{}, error) {
201 ep := arvados.EndpointCollectionProvenance
202 var resp map[string]interface{}
203 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
207 func (conn *Conn) CollectionUsedBy(ctx context.Context, options arvados.GetOptions) (map[string]interface{}, error) {
208 ep := arvados.EndpointCollectionUsedBy
209 var resp map[string]interface{}
210 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
214 func (conn *Conn) CollectionDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.Collection, error) {
215 ep := arvados.EndpointCollectionDelete
216 var resp arvados.Collection
217 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
221 func (conn *Conn) CollectionTrash(ctx context.Context, options arvados.DeleteOptions) (arvados.Collection, error) {
222 ep := arvados.EndpointCollectionTrash
223 var resp arvados.Collection
224 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
228 func (conn *Conn) CollectionUntrash(ctx context.Context, options arvados.UntrashOptions) (arvados.Collection, error) {
229 ep := arvados.EndpointCollectionUntrash
230 var resp arvados.Collection
231 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
235 func (conn *Conn) ContainerCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Container, error) {
236 ep := arvados.EndpointContainerCreate
237 var resp arvados.Container
238 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
242 func (conn *Conn) ContainerUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.Container, error) {
243 ep := arvados.EndpointContainerUpdate
244 var resp arvados.Container
245 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
249 func (conn *Conn) ContainerGet(ctx context.Context, options arvados.GetOptions) (arvados.Container, error) {
250 ep := arvados.EndpointContainerGet
251 var resp arvados.Container
252 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
256 func (conn *Conn) ContainerList(ctx context.Context, options arvados.ListOptions) (arvados.ContainerList, error) {
257 ep := arvados.EndpointContainerList
258 var resp arvados.ContainerList
259 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
263 func (conn *Conn) ContainerDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.Container, error) {
264 ep := arvados.EndpointContainerDelete
265 var resp arvados.Container
266 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
270 func (conn *Conn) ContainerLock(ctx context.Context, options arvados.GetOptions) (arvados.Container, error) {
271 ep := arvados.EndpointContainerLock
272 var resp arvados.Container
273 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
277 func (conn *Conn) ContainerUnlock(ctx context.Context, options arvados.GetOptions) (arvados.Container, error) {
278 ep := arvados.EndpointContainerUnlock
279 var resp arvados.Container
280 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
284 func (conn *Conn) SpecimenCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Specimen, error) {
285 ep := arvados.EndpointSpecimenCreate
286 var resp arvados.Specimen
287 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
291 func (conn *Conn) SpecimenUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.Specimen, error) {
292 ep := arvados.EndpointSpecimenUpdate
293 var resp arvados.Specimen
294 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
298 func (conn *Conn) SpecimenGet(ctx context.Context, options arvados.GetOptions) (arvados.Specimen, error) {
299 ep := arvados.EndpointSpecimenGet
300 var resp arvados.Specimen
301 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
305 func (conn *Conn) SpecimenList(ctx context.Context, options arvados.ListOptions) (arvados.SpecimenList, error) {
306 ep := arvados.EndpointSpecimenList
307 var resp arvados.SpecimenList
308 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
312 func (conn *Conn) SpecimenDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.Specimen, error) {
313 ep := arvados.EndpointSpecimenDelete
314 var resp arvados.Specimen
315 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
319 func (conn *Conn) UserCreate(ctx context.Context, options arvados.CreateOptions) (arvados.User, error) {
320 ep := arvados.EndpointUserCreate
321 var resp arvados.User
322 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
325 func (conn *Conn) UserUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.User, error) {
326 ep := arvados.EndpointUserUpdate
327 var resp arvados.User
328 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
331 func (conn *Conn) UserUpdateUUID(ctx context.Context, options arvados.UpdateUUIDOptions) (arvados.User, error) {
332 ep := arvados.EndpointUserUpdateUUID
333 var resp arvados.User
334 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
337 func (conn *Conn) UserMerge(ctx context.Context, options arvados.UserMergeOptions) (arvados.User, error) {
338 ep := arvados.EndpointUserMerge
339 var resp arvados.User
340 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
343 func (conn *Conn) UserActivate(ctx context.Context, options arvados.UserActivateOptions) (arvados.User, error) {
344 ep := arvados.EndpointUserActivate
345 var resp arvados.User
346 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
349 func (conn *Conn) UserSetup(ctx context.Context, options arvados.UserSetupOptions) (map[string]interface{}, error) {
350 ep := arvados.EndpointUserSetup
351 var resp map[string]interface{}
352 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
355 func (conn *Conn) UserUnsetup(ctx context.Context, options arvados.GetOptions) (arvados.User, error) {
356 ep := arvados.EndpointUserUnsetup
357 var resp arvados.User
358 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
361 func (conn *Conn) UserGet(ctx context.Context, options arvados.GetOptions) (arvados.User, error) {
362 ep := arvados.EndpointUserGet
363 var resp arvados.User
364 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
367 func (conn *Conn) UserGetCurrent(ctx context.Context, options arvados.GetOptions) (arvados.User, error) {
368 ep := arvados.EndpointUserGetCurrent
369 var resp arvados.User
370 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
373 func (conn *Conn) UserGetSystem(ctx context.Context, options arvados.GetOptions) (arvados.User, error) {
374 ep := arvados.EndpointUserGetSystem
375 var resp arvados.User
376 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
379 func (conn *Conn) UserList(ctx context.Context, options arvados.ListOptions) (arvados.UserList, error) {
380 ep := arvados.EndpointUserList
381 var resp arvados.UserList
382 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
385 func (conn *Conn) UserDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.User, error) {
386 ep := arvados.EndpointUserDelete
387 var resp arvados.User
388 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
392 func (conn *Conn) APIClientAuthorizationCurrent(ctx context.Context, options arvados.GetOptions) (arvados.APIClientAuthorization, error) {
393 ep := arvados.EndpointAPIClientAuthorizationCurrent
394 var resp arvados.APIClientAuthorization
395 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
399 type UserSessionAuthInfo struct {
400 Email string `json:"email"`
401 AlternateEmails []string `json:"alternate_emails"`
402 FirstName string `json:"first_name"`
403 LastName string `json:"last_name"`
404 Username string `json:"username"`
407 type UserSessionCreateOptions struct {
408 AuthInfo UserSessionAuthInfo `json:"auth_info"`
409 ReturnTo string `json:"return_to"`
412 func (conn *Conn) UserSessionCreate(ctx context.Context, options UserSessionCreateOptions) (arvados.LoginResponse, error) {
413 ep := arvados.APIEndpoint{Method: "POST", Path: "auth/controller/callback"}
414 var resp arvados.LoginResponse
415 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
419 func (conn *Conn) UserBatchUpdate(ctx context.Context, options arvados.UserBatchUpdateOptions) (arvados.UserList, error) {
420 ep := arvados.APIEndpoint{Method: "PATCH", Path: "arvados/v1/users/batch_update"}
421 var resp arvados.UserList
422 err := conn.requestAndDecode(ctx, &resp, ep, nil, options)