// AddCmd adds a command for execution on this range. The command's // affected keys are verified to be contained within the range and the // range's leadership is confirmed. The command is then dispatched // either along the read-only execution path or the read-write Raft // command queue. func (r *Range) AddCmd(ctx context.Context, call proto.Call) error { args := call.Args // TODO(tschottdorf) Some (internal) requests go here directly, so they // won't be traced. trace := tracer.FromCtx(ctx) // Differentiate between admin, read-only and read-write. var reply proto.Response var err error if proto.IsAdmin(args) { defer trace.Epoch("admin path")() reply, err = r.addAdminCmd(ctx, args) } else if proto.IsReadOnly(args) { defer trace.Epoch("read-only path")() reply, err = r.addReadOnlyCmd(ctx, args) } else if proto.IsWrite(args) { defer trace.Epoch("read-write path")() reply, err = r.addWriteCmd(ctx, args, nil) } else { panic(fmt.Sprintf("don't know how to handle command %T", args)) } if reply != nil { gogoproto.Merge(call.Reply, reply) } if err != nil { replyHeader := call.Reply.Header() if replyHeader.Error != nil { panic("the world is on fire") } replyHeader.SetGoError(err) } return err }
// AddCmd adds a command for execution on this range. The command's // affected keys are verified to be contained within the range and the // range's leadership is confirmed. The command is then dispatched // either along the read-only execution path or the read-write Raft // command queue. func (r *Range) AddCmd(ctx context.Context, call proto.Call) error { args, reply := call.Args, call.Reply // Differentiate between admin, read-only and read-write. if proto.IsAdmin(args) { return r.addAdminCmd(ctx, args, reply) } else if proto.IsReadOnly(args) { return r.addReadOnlyCmd(ctx, args, reply) } return r.addWriteCmd(ctx, args, reply, nil) }
// AddCmd adds a command for execution on this range. The command's // affected keys are verified to be contained within the range and the // range's leadership is confirmed. The command is then dispatched // either along the read-only execution path or the read-write Raft // command queue. func (r *Range) AddCmd(ctx context.Context, call proto.Call) error { args, reply := call.Args, call.Reply // TODO(tschottdorf) Some (internal) requests go here directly, so they // won't be traced. trace := tracer.FromCtx(ctx) // Differentiate between admin, read-only and read-write. if proto.IsAdmin(args) { defer trace.Epoch("admin path")() return r.addAdminCmd(ctx, args, reply) } else if proto.IsReadOnly(args) { defer trace.Epoch("read path")() return r.addReadOnlyCmd(ctx, args, reply) } return r.addWriteCmd(ctx, args, reply, nil) }
// AddCmd adds a command for execution on this range. The command's // affected keys are verified to be contained within the range and the // range's leadership is confirmed. The command is then dispatched // either along the read-only execution path or the read-write Raft // command queue. If wait is false, read-write commands are added to // Raft without waiting for their completion. func (r *Range) AddCmd(ctx context.Context, call proto.Call, wait bool) error { args, reply := call.Args, call.Reply header := args.Header() if !r.ContainsKeyRange(header.Key, header.EndKey) { err := proto.NewRangeKeyMismatchError(header.Key, header.EndKey, r.Desc()) reply.Header().SetGoError(err) return err } // Differentiate between admin, read-only and read-write. if proto.IsAdmin(args) { return r.addAdminCmd(ctx, args, reply) } else if proto.IsReadOnly(args) { return r.addReadOnlyCmd(ctx, args, reply) } return r.addWriteCmd(ctx, args, reply, wait) }
// AddCmd adds a command for execution on this range. The command's // affected keys are verified to be contained within the range and the // range's leadership is confirmed. The command is then dispatched // either along the read-only execution path or the read-write Raft // command queue. func (r *Replica) AddCmd(ctx context.Context, args proto.Request) (proto.Response, error) { // TODO(tschottdorf) Some (internal) requests go here directly, so they // won't be traced. trace := tracer.FromCtx(ctx) // Differentiate between admin, read-only and read-write. var reply proto.Response var err error if proto.IsAdmin(args) { defer trace.Epoch("admin path")() reply, err = r.addAdminCmd(ctx, args) } else if proto.IsReadOnly(args) { defer trace.Epoch("read-only path")() reply, err = r.addReadOnlyCmd(ctx, args) } else if proto.IsWrite(args) { defer trace.Epoch("read-write path")() reply, err = r.addWriteCmd(ctx, args, nil) } else { panic(fmt.Sprintf("don't know how to handle command %T", args)) } return reply, err }
// verifyPermissions verifies that the requesting user (header.User) // has permission to read/write (capabilities depend on method // name). In the event that multiple permission configs apply to the // key range implicated by the command, the lowest common denominator // for permission. For example, if a scan crosses two permission // configs, both configs must allow read permissions or the entire // scan will fail. func (ds *DistSender) verifyPermissions(args proto.Request) error { // The root user can always proceed. header := args.Header() if header.User == storage.UserRoot { return nil } // Check for admin methods. if proto.IsAdmin(args) { if header.User != storage.UserRoot { return util.Errorf("user %q cannot invoke admin command %s", header.User, args.Method()) } return nil } // Get permissions map from gossip. configMap, err := ds.gossip.GetInfo(gossip.KeyConfigPermission) if err != nil { return util.Errorf("permissions not available via gossip") } if configMap == nil { return util.Errorf("perm configs not available; cannot execute %s", args.Method()) } permMap := configMap.(storage.PrefixConfigMap) headerEnd := header.EndKey if len(headerEnd) == 0 { headerEnd = header.Key } // Visit PermConfig(s) which apply to the method's key range. // - For each perm config which the range covers, verify read or writes // are allowed as method requires. // - Verify the permissions hierarchically; that is, if permissions aren't // granted at the longest prefix, try next longest, then next, etc., up // to and including the default prefix. // // TODO(spencer): it might make sense to visit prefixes from the // shortest to longest instead for performance. Keep an eye on profiling // for this code path as permission sets grow large. return permMap.VisitPrefixes(header.Key, headerEnd, func(start, end proto.Key, config interface{}) (bool, error) { hasPerm := false if err := permMap.VisitPrefixesHierarchically(start, func(start, end proto.Key, config interface{}) (bool, error) { perm := config.(*proto.PermConfig) if proto.IsRead(args) && !perm.CanRead(header.User) { return false, nil } if proto.IsWrite(args) && !perm.CanWrite(header.User) { return false, nil } // Return done = true, as permissions have been granted by this config. hasPerm = true return true, nil }); err != nil { return false, err } if !hasPerm { if len(header.EndKey) == 0 { return false, util.Errorf("user %q cannot invoke %s at %q", header.User, args.Method(), start) } return false, util.Errorf("user %q cannot invoke %s at %q-%q", header.User, args.Method(), start, end) } return false, nil }) }