X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/e459f4e2d40762f67ffedafbe988c8da6f4f04d4..2ba6cc7a5e4bfd05cd51e8ab22be2a99a883349d:/lib/controller/federation/conn.go diff --git a/lib/controller/federation/conn.go b/lib/controller/federation/conn.go index 56f117ee78..418b6811be 100644 --- a/lib/controller/federation/conn.go +++ b/lib/controller/federation/conn.go @@ -7,7 +7,6 @@ package federation import ( "bytes" "context" - "crypto/md5" "encoding/json" "errors" "fmt" @@ -35,7 +34,7 @@ func New(cluster *arvados.Cluster) *Conn { local := localdb.NewConn(cluster) remotes := map[string]backend{} for id, remote := range cluster.RemoteClusters { - if !remote.Proxy { + if !remote.Proxy || id == cluster.ClusterID { continue } conn := rpc.NewConn(id, &url.URL{Scheme: remote.Scheme, Host: remote.Host}, remote.Insecure, saltedTokenProvider(local, id)) @@ -169,26 +168,6 @@ func rewriteManifest(mt, remoteID string) string { }) } -// this could be in sdk/go/arvados -func portableDataHash(mt string) string { - h := md5.New() - blkRe := regexp.MustCompile(`^ [0-9a-f]{32}\+\d+`) - size := 0 - _ = regexp.MustCompile(` ?[^ ]*`).ReplaceAllFunc([]byte(mt), func(tok []byte) []byte { - if m := blkRe.Find(tok); m != nil { - // write hash+size, ignore remaining block hints - tok = m - } - n, err := h.Write(tok) - if err != nil { - panic(err) - } - size += n - return nil - }) - return fmt.Sprintf("%x+%d", h.Sum(nil), size) -} - func (conn *Conn) ConfigGet(ctx context.Context) (json.RawMessage, error) { var buf bytes.Buffer err := config.ExportJSON(&buf, conn.cluster) @@ -269,7 +248,7 @@ func (conn *Conn) CollectionGet(ctx context.Context, options arvados.GetOptions) // options.UUID is either hash+size or // hash+size+hints; only hash+size need to // match the computed PDH. - if pdh := portableDataHash(c.ManifestText); pdh != options.UUID && !strings.HasPrefix(options.UUID, pdh+"+") { + if pdh := arvados.PortableDataHash(c.ManifestText); pdh != options.UUID && !strings.HasPrefix(options.UUID, pdh+"+") { err = httpErrorf(http.StatusBadGateway, "bad portable data hash %q received from remote %q (expected %q)", pdh, remoteID, options.UUID) ctxlog.FromContext(ctx).Warn(err) return err @@ -365,85 +344,97 @@ func (conn *Conn) SpecimenDelete(ctx context.Context, options arvados.DeleteOpti } var userAttrsCachedFromLoginCluster = map[string]bool{ - "created_at": true, - "email": true, - "first_name": true, - "is_active": true, - "is_admin": true, - "last_name": true, - "modified_at": true, - "modified_by_client_uuid": true, - "modified_by_user_uuid": true, - "prefs": true, - "username": true, - - "etag": false, - "full_name": false, - "identity_url": false, - "is_invited": false, - "owner_uuid": false, - "uuid": false, - "writable_by": false, -} - -func (conn *Conn) UserList(ctx context.Context, options arvados.ListOptions) (arvados.UserList, error) { + "created_at": true, + "email": true, + "first_name": true, + "is_active": true, + "is_admin": true, + "last_name": true, + "modified_at": true, + "prefs": true, + "username": true, + + "etag": false, + "full_name": false, + "identity_url": false, + "is_invited": false, + "modified_by_client_uuid": false, + "modified_by_user_uuid": false, + "owner_uuid": false, + "uuid": false, + "writable_by": false, +} + +func (conn *Conn) batchUpdateUsers(ctx context.Context, + options arvados.ListOptions, + items []arvados.User) (err error) { + + id := conn.cluster.Login.LoginCluster logger := ctxlog.FromContext(ctx) - if id := conn.cluster.Login.LoginCluster; id != "" && id != conn.cluster.ClusterID { - resp, err := conn.chooseBackend(id).UserList(ctx, options) - if err != nil { - return resp, err + batchOpts := arvados.UserBatchUpdateOptions{Updates: map[string]map[string]interface{}{}} + for _, user := range items { + if !strings.HasPrefix(user.UUID, id) { + continue + } + logger.Debugf("cache user info for uuid %q", user.UUID) + + // If the remote cluster has null timestamps + // (e.g., test server with incomplete + // fixtures) use dummy timestamps (instead of + // the zero time, which causes a Rails API + // error "year too big to marshal: 1 UTC"). + if user.ModifiedAt.IsZero() { + user.ModifiedAt = time.Now() + } + if user.CreatedAt.IsZero() { + user.CreatedAt = time.Now() } - batchOpts := arvados.UserBatchUpdateOptions{Updates: map[string]map[string]interface{}{}} - for _, user := range resp.Items { - if !strings.HasPrefix(user.UUID, id) { - continue - } - logger.Debugf("cache user info for uuid %q", user.UUID) - - // If the remote cluster has null timestamps - // (e.g., test server with incomplete - // fixtures) use dummy timestamps (instead of - // the zero time, which causes a Rails API - // error "year too big to marshal: 1 UTC"). - if user.ModifiedAt.IsZero() { - user.ModifiedAt = time.Now() - } - if user.CreatedAt.IsZero() { - user.CreatedAt = time.Now() - } - var allFields map[string]interface{} - buf, err := json.Marshal(user) - if err != nil { - return arvados.UserList{}, fmt.Errorf("error encoding user record from remote response: %s", err) - } - err = json.Unmarshal(buf, &allFields) - if err != nil { - return arvados.UserList{}, fmt.Errorf("error transcoding user record from remote response: %s", err) - } - updates := allFields - if len(options.Select) > 0 { - updates = map[string]interface{}{} - for _, k := range options.Select { - if v, ok := allFields[k]; ok && userAttrsCachedFromLoginCluster[k] { - updates[k] = v - } + var allFields map[string]interface{} + buf, err := json.Marshal(user) + if err != nil { + return fmt.Errorf("error encoding user record from remote response: %s", err) + } + err = json.Unmarshal(buf, &allFields) + if err != nil { + return fmt.Errorf("error transcoding user record from remote response: %s", err) + } + updates := allFields + if len(options.Select) > 0 { + updates = map[string]interface{}{} + for _, k := range options.Select { + if v, ok := allFields[k]; ok && userAttrsCachedFromLoginCluster[k] { + updates[k] = v } - } else { - for k := range updates { - if !userAttrsCachedFromLoginCluster[k] { - delete(updates, k) - } + } + } else { + for k := range updates { + if !userAttrsCachedFromLoginCluster[k] { + delete(updates, k) } } - batchOpts.Updates[user.UUID] = updates } - if len(batchOpts.Updates) > 0 { - ctxRoot := auth.NewContext(ctx, &auth.Credentials{Tokens: []string{conn.cluster.SystemRootToken}}) - _, err = conn.local.UserBatchUpdate(ctxRoot, batchOpts) - if err != nil { - return arvados.UserList{}, fmt.Errorf("error updating local user records: %s", err) - } + batchOpts.Updates[user.UUID] = updates + } + if len(batchOpts.Updates) > 0 { + ctxRoot := auth.NewContext(ctx, &auth.Credentials{Tokens: []string{conn.cluster.SystemRootToken}}) + _, err = conn.local.UserBatchUpdate(ctxRoot, batchOpts) + if err != nil { + return fmt.Errorf("error updating local user records: %s", err) + } + } + return nil +} + +func (conn *Conn) UserList(ctx context.Context, options arvados.ListOptions) (arvados.UserList, error) { + if id := conn.cluster.Login.LoginCluster; id != "" && id != conn.cluster.ClusterID && !options.BypassFederation { + resp, err := conn.chooseBackend(id).UserList(ctx, options) + if err != nil { + return resp, err + } + err = conn.batchUpdateUsers(ctx, options, resp.Items) + if err != nil { + return arvados.UserList{}, err } return resp, nil } else { @@ -456,15 +447,18 @@ func (conn *Conn) UserCreate(ctx context.Context, options arvados.CreateOptions) } func (conn *Conn) UserUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.User, error) { + if options.BypassFederation { + return conn.local.UserUpdate(ctx, options) + } return conn.chooseBackend(options.UUID).UserUpdate(ctx, options) } func (conn *Conn) UserUpdateUUID(ctx context.Context, options arvados.UpdateUUIDOptions) (arvados.User, error) { - return conn.chooseBackend(options.UUID).UserUpdateUUID(ctx, options) + return conn.local.UserUpdateUUID(ctx, options) } func (conn *Conn) UserMerge(ctx context.Context, options arvados.UserMergeOptions) (arvados.User, error) { - return conn.chooseBackend(options.OldUserUUID).UserMerge(ctx, options) + return conn.local.UserMerge(ctx, options) } func (conn *Conn) UserActivate(ctx context.Context, options arvados.UserActivateOptions) (arvados.User, error) { @@ -499,6 +493,10 @@ func (conn *Conn) UserBatchUpdate(ctx context.Context, options arvados.UserBatch return conn.local.UserBatchUpdate(ctx, options) } +func (conn *Conn) UserAuthenticate(ctx context.Context, options arvados.UserAuthenticateOptions) (arvados.APIClientAuthorization, error) { + return conn.local.UserAuthenticate(ctx, options) +} + func (conn *Conn) APIClientAuthorizationCurrent(ctx context.Context, options arvados.GetOptions) (arvados.APIClientAuthorization, error) { return conn.chooseBackend(options.UUID).APIClientAuthorizationCurrent(ctx, options) }