github.com/clly/consul@v1.4.5/agent/consul/kvs_endpoint.go (about)

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/armon/go-metrics"
     8  	"github.com/hashicorp/consul/acl"
     9  	"github.com/hashicorp/consul/agent/consul/state"
    10  	"github.com/hashicorp/consul/agent/structs"
    11  	"github.com/hashicorp/consul/api"
    12  	"github.com/hashicorp/consul/sentinel"
    13  	"github.com/hashicorp/go-memdb"
    14  )
    15  
    16  // KVS endpoint is used to manipulate the Key-Value store
    17  type KVS struct {
    18  	srv *Server
    19  }
    20  
    21  // preApply does all the verification of a KVS update that is performed BEFORE
    22  // we submit as a Raft log entry. This includes enforcing the lock delay which
    23  // must only be done on the leader.
    24  func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs.DirEntry) (bool, error) {
    25  	// Verify the entry.
    26  
    27  	if dirEnt.Key == "" && op != api.KVDeleteTree {
    28  		return false, fmt.Errorf("Must provide key")
    29  	}
    30  
    31  	// Apply the ACL policy if any.
    32  	if rule != nil {
    33  		switch op {
    34  		case api.KVDeleteTree:
    35  			if !rule.KeyWritePrefix(dirEnt.Key) {
    36  				return false, acl.ErrPermissionDenied
    37  			}
    38  
    39  		case api.KVGet, api.KVGetTree:
    40  			// Filtering for GETs is done on the output side.
    41  
    42  		case api.KVCheckSession, api.KVCheckIndex:
    43  			// These could reveal information based on the outcome
    44  			// of the transaction, and they operate on individual
    45  			// keys so we check them here.
    46  			if !rule.KeyRead(dirEnt.Key) {
    47  				return false, acl.ErrPermissionDenied
    48  			}
    49  
    50  		default:
    51  			scope := func() map[string]interface{} {
    52  				return sentinel.ScopeKVUpsert(dirEnt.Key, dirEnt.Value, dirEnt.Flags)
    53  			}
    54  			if !rule.KeyWrite(dirEnt.Key, scope) {
    55  				return false, acl.ErrPermissionDenied
    56  			}
    57  		}
    58  	}
    59  
    60  	// If this is a lock, we must check for a lock-delay. Since lock-delay
    61  	// is based on wall-time, each peer would expire the lock-delay at a slightly
    62  	// different time. This means the enforcement of lock-delay cannot be done
    63  	// after the raft log is committed as it would lead to inconsistent FSMs.
    64  	// Instead, the lock-delay must be enforced before commit. This means that
    65  	// only the wall-time of the leader node is used, preventing any inconsistencies.
    66  	if op == api.KVLock {
    67  		state := srv.fsm.State()
    68  		expires := state.KVSLockDelay(dirEnt.Key)
    69  		if expires.After(time.Now()) {
    70  			srv.logger.Printf("[WARN] consul.kvs: Rejecting lock of %s due to lock-delay until %v",
    71  				dirEnt.Key, expires)
    72  			return false, nil
    73  		}
    74  	}
    75  
    76  	return true, nil
    77  }
    78  
    79  // Apply is used to apply a KVS update request to the data store.
    80  func (k *KVS) Apply(args *structs.KVSRequest, reply *bool) error {
    81  	if done, err := k.srv.forward("KVS.Apply", args, args, reply); done {
    82  		return err
    83  	}
    84  	defer metrics.MeasureSince([]string{"kvs", "apply"}, time.Now())
    85  
    86  	// Perform the pre-apply checks.
    87  	acl, err := k.srv.ResolveToken(args.Token)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	ok, err := kvsPreApply(k.srv, acl, args.Op, &args.DirEnt)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	if !ok {
    96  		*reply = false
    97  		return nil
    98  	}
    99  
   100  	// Apply the update.
   101  	resp, err := k.srv.raftApply(structs.KVSRequestType, args)
   102  	if err != nil {
   103  		k.srv.logger.Printf("[ERR] consul.kvs: Apply failed: %v", err)
   104  		return err
   105  	}
   106  	if respErr, ok := resp.(error); ok {
   107  		return respErr
   108  	}
   109  
   110  	// Check if the return type is a bool.
   111  	if respBool, ok := resp.(bool); ok {
   112  		*reply = respBool
   113  	}
   114  	return nil
   115  }
   116  
   117  // Get is used to lookup a single key.
   118  func (k *KVS) Get(args *structs.KeyRequest, reply *structs.IndexedDirEntries) error {
   119  	if done, err := k.srv.forward("KVS.Get", args, args, reply); done {
   120  		return err
   121  	}
   122  
   123  	aclRule, err := k.srv.ResolveToken(args.Token)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	return k.srv.blockingQuery(
   128  		&args.QueryOptions,
   129  		&reply.QueryMeta,
   130  		func(ws memdb.WatchSet, state *state.Store) error {
   131  			index, ent, err := state.KVSGet(ws, args.Key)
   132  			if err != nil {
   133  				return err
   134  			}
   135  			if aclRule != nil && !aclRule.KeyRead(args.Key) {
   136  				return acl.ErrPermissionDenied
   137  			}
   138  
   139  			if ent == nil {
   140  				// Must provide non-zero index to prevent blocking
   141  				// Index 1 is impossible anyways (due to Raft internals)
   142  				if index == 0 {
   143  					reply.Index = 1
   144  				} else {
   145  					reply.Index = index
   146  				}
   147  				reply.Entries = nil
   148  			} else {
   149  				reply.Index = ent.ModifyIndex
   150  				reply.Entries = structs.DirEntries{ent}
   151  			}
   152  			return nil
   153  		})
   154  }
   155  
   156  // List is used to list all keys with a given prefix.
   157  func (k *KVS) List(args *structs.KeyRequest, reply *structs.IndexedDirEntries) error {
   158  	if done, err := k.srv.forward("KVS.List", args, args, reply); done {
   159  		return err
   160  	}
   161  
   162  	aclToken, err := k.srv.ResolveToken(args.Token)
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	if aclToken != nil && k.srv.config.ACLEnableKeyListPolicy && !aclToken.KeyList(args.Key) {
   168  		return acl.ErrPermissionDenied
   169  	}
   170  
   171  	return k.srv.blockingQuery(
   172  		&args.QueryOptions,
   173  		&reply.QueryMeta,
   174  		func(ws memdb.WatchSet, state *state.Store) error {
   175  			index, ent, err := state.KVSList(ws, args.Key)
   176  			if err != nil {
   177  				return err
   178  			}
   179  			if aclToken != nil {
   180  				ent = FilterDirEnt(aclToken, ent)
   181  			}
   182  
   183  			if len(ent) == 0 {
   184  				// Must provide non-zero index to prevent blocking
   185  				// Index 1 is impossible anyways (due to Raft internals)
   186  				if index == 0 {
   187  					reply.Index = 1
   188  				} else {
   189  					reply.Index = index
   190  				}
   191  				reply.Entries = nil
   192  			} else {
   193  				reply.Index = index
   194  				reply.Entries = ent
   195  			}
   196  			return nil
   197  		})
   198  }
   199  
   200  // ListKeys is used to list all keys with a given prefix to a separator.
   201  func (k *KVS) ListKeys(args *structs.KeyListRequest, reply *structs.IndexedKeyList) error {
   202  	if done, err := k.srv.forward("KVS.ListKeys", args, args, reply); done {
   203  		return err
   204  	}
   205  
   206  	aclToken, err := k.srv.ResolveToken(args.Token)
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	if aclToken != nil && k.srv.config.ACLEnableKeyListPolicy && !aclToken.KeyList(args.Prefix) {
   212  		return acl.ErrPermissionDenied
   213  	}
   214  
   215  	return k.srv.blockingQuery(
   216  		&args.QueryOptions,
   217  		&reply.QueryMeta,
   218  		func(ws memdb.WatchSet, state *state.Store) error {
   219  			index, keys, err := state.KVSListKeys(ws, args.Prefix, args.Seperator)
   220  			if err != nil {
   221  				return err
   222  			}
   223  
   224  			// Must provide non-zero index to prevent blocking
   225  			// Index 1 is impossible anyways (due to Raft internals)
   226  			if index == 0 {
   227  				reply.Index = 1
   228  			} else {
   229  				reply.Index = index
   230  			}
   231  
   232  			if aclToken != nil {
   233  				keys = FilterKeys(aclToken, keys)
   234  			}
   235  			reply.Keys = keys
   236  			return nil
   237  		})
   238  }