Merge branch 'main' from workbench2.git
[arvados.git] / lib / controller / federation / conn.go
index ffb150bf26aa148b511f4bbde98305469ffef5df..b4b747644061774ef6b4fb6b837e2035ad3774b0 100644 (file)
@@ -23,16 +23,18 @@ import (
        "git.arvados.org/arvados.git/sdk/go/auth"
        "git.arvados.org/arvados.git/sdk/go/ctxlog"
        "git.arvados.org/arvados.git/sdk/go/health"
+       "github.com/jmoiron/sqlx"
 )
 
 type Conn struct {
+       bgCtx   context.Context
        cluster *arvados.Cluster
        local   backend
        remotes map[string]backend
 }
 
-func New(cluster *arvados.Cluster, healthFuncs *map[string]health.Func) *Conn {
-       local := localdb.NewConn(cluster)
+func New(bgCtx context.Context, cluster *arvados.Cluster, healthFuncs *map[string]health.Func, getdb func(context.Context) (*sqlx.DB, error)) *Conn {
+       local := localdb.NewConn(bgCtx, cluster, getdb)
        remotes := map[string]backend{}
        for id, remote := range cluster.RemoteClusters {
                if !remote.Proxy || id == cluster.ClusterID {
@@ -51,6 +53,7 @@ func New(cluster *arvados.Cluster, healthFuncs *map[string]health.Func) *Conn {
        }
 
        return &Conn{
+               bgCtx:   bgCtx,
                cluster: cluster,
                local:   local,
                remotes: remotes,
@@ -175,20 +178,29 @@ func (conn *Conn) tryLocalThenRemotes(ctx context.Context, forwardedFor string,
                        errchan <- fn(ctx, remoteID, be)
                }()
        }
-       all404 := true
+       returncode := http.StatusNotFound
        var errs []error
        for i := 0; i < cap(errchan); i++ {
                err := <-errchan
                if err == nil {
                        return nil
                }
-               all404 = all404 && errStatus(err) == http.StatusNotFound
                errs = append(errs, err)
+               if code := errStatus(err); code >= 500 || code == http.StatusTooManyRequests {
+                       // If any of the remotes have a retryable
+                       // error (and none succeed) we'll return 502.
+                       returncode = http.StatusBadGateway
+               } else if code != http.StatusNotFound && returncode != http.StatusBadGateway {
+                       // If some of the remotes have non-retryable
+                       // non-404 errors (and none succeed or have
+                       // retryable errors) we'll return 422.
+                       returncode = http.StatusUnprocessableEntity
+               }
        }
-       if all404 {
+       if returncode == http.StatusNotFound {
                return notFoundError{}
        }
-       return httpErrorf(http.StatusBadGateway, "errors: %v", errs)
+       return httpErrorf(returncode, "errors: %v", errs)
 }
 
 func (conn *Conn) CollectionCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Collection, error) {
@@ -267,6 +279,26 @@ func (conn *Conn) Logout(ctx context.Context, options arvados.LogoutOptions) (ar
        return arvados.LogoutResponse{RedirectLocation: target.String()}, nil
 }
 
+func (conn *Conn) AuthorizedKeyCreate(ctx context.Context, options arvados.CreateOptions) (arvados.AuthorizedKey, error) {
+       return conn.chooseBackend(options.ClusterID).AuthorizedKeyCreate(ctx, options)
+}
+
+func (conn *Conn) AuthorizedKeyUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.AuthorizedKey, error) {
+       return conn.chooseBackend(options.UUID).AuthorizedKeyUpdate(ctx, options)
+}
+
+func (conn *Conn) AuthorizedKeyGet(ctx context.Context, options arvados.GetOptions) (arvados.AuthorizedKey, error) {
+       return conn.chooseBackend(options.UUID).AuthorizedKeyGet(ctx, options)
+}
+
+func (conn *Conn) AuthorizedKeyList(ctx context.Context, options arvados.ListOptions) (arvados.AuthorizedKeyList, error) {
+       return conn.generated_AuthorizedKeyList(ctx, options)
+}
+
+func (conn *Conn) AuthorizedKeyDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.AuthorizedKey, error) {
+       return conn.chooseBackend(options.UUID).AuthorizedKeyDelete(ctx, options)
+}
+
 func (conn *Conn) CollectionGet(ctx context.Context, options arvados.GetOptions) (arvados.Collection, error) {
        if len(options.UUID) == 27 {
                // UUID is really a UUID
@@ -276,6 +308,9 @@ func (conn *Conn) CollectionGet(ctx context.Context, options arvados.GetOptions)
                }
                return c, err
        }
+       if len(options.UUID) < 34 || options.UUID[32] != '+' {
+               return arvados.Collection{}, httpErrorf(http.StatusNotFound, "invalid UUID or PDH %q", options.UUID)
+       }
        // UUID is a PDH
        first := make(chan arvados.Collection, 1)
        err := conn.tryLocalThenRemotes(ctx, options.ForwardedFor, func(ctx context.Context, remoteID string, be backend) error {
@@ -359,6 +394,10 @@ func (conn *Conn) ContainerUpdate(ctx context.Context, options arvados.UpdateOpt
        return conn.chooseBackend(options.UUID).ContainerUpdate(ctx, options)
 }
 
+func (conn *Conn) ContainerPriorityUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.Container, error) {
+       return conn.chooseBackend(options.UUID).ContainerPriorityUpdate(ctx, options)
+}
+
 func (conn *Conn) ContainerGet(ctx context.Context, options arvados.GetOptions) (arvados.Container, error) {
        return conn.chooseBackend(options.UUID).ContainerGet(ctx, options)
 }
@@ -445,6 +484,10 @@ func (conn *Conn) ContainerRequestDelete(ctx context.Context, options arvados.De
        return conn.chooseBackend(options.UUID).ContainerRequestDelete(ctx, options)
 }
 
+func (conn *Conn) ContainerRequestLog(ctx context.Context, options arvados.ContainerLogOptions) (http.Handler, error) {
+       return conn.chooseBackend(options.UUID).ContainerRequestLog(ctx, options)
+}
+
 func (conn *Conn) GroupCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Group, error) {
        return conn.chooseBackend(options.ClusterID).GroupCreate(ctx, options)
 }
@@ -512,6 +555,26 @@ func (conn *Conn) LinkDelete(ctx context.Context, options arvados.DeleteOptions)
        return conn.chooseBackend(options.UUID).LinkDelete(ctx, options)
 }
 
+func (conn *Conn) LogCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Log, error) {
+       return conn.chooseBackend(options.ClusterID).LogCreate(ctx, options)
+}
+
+func (conn *Conn) LogUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.Log, error) {
+       return conn.chooseBackend(options.UUID).LogUpdate(ctx, options)
+}
+
+func (conn *Conn) LogGet(ctx context.Context, options arvados.GetOptions) (arvados.Log, error) {
+       return conn.chooseBackend(options.UUID).LogGet(ctx, options)
+}
+
+func (conn *Conn) LogList(ctx context.Context, options arvados.ListOptions) (arvados.LogList, error) {
+       return conn.generated_LogList(ctx, options)
+}
+
+func (conn *Conn) LogDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.Log, error) {
+       return conn.chooseBackend(options.UUID).LogDelete(ctx, options)
+}
+
 func (conn *Conn) SpecimenList(ctx context.Context, options arvados.ListOptions) (arvados.SpecimenList, error) {
        return conn.generated_SpecimenList(ctx, options)
 }