github.com/Iqoqo/consul@v1.4.5/agent/consul/session_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/go-memdb"
    12  	"github.com/hashicorp/go-uuid"
    13  )
    14  
    15  // Session endpoint is used to manipulate sessions for KV
    16  type Session struct {
    17  	srv *Server
    18  }
    19  
    20  // Apply is used to apply a modifying request to the data store. This should
    21  // only be used for operations that modify the data
    22  func (s *Session) Apply(args *structs.SessionRequest, reply *string) error {
    23  	if done, err := s.srv.forward("Session.Apply", args, args, reply); done {
    24  		return err
    25  	}
    26  	defer metrics.MeasureSince([]string{"session", "apply"}, time.Now())
    27  
    28  	// Verify the args
    29  	if args.Session.ID == "" && args.Op == structs.SessionDestroy {
    30  		return fmt.Errorf("Must provide ID")
    31  	}
    32  	if args.Session.Node == "" && args.Op == structs.SessionCreate {
    33  		return fmt.Errorf("Must provide Node")
    34  	}
    35  
    36  	// Fetch the ACL token, if any, and apply the policy.
    37  	rule, err := s.srv.ResolveToken(args.Token)
    38  	if err != nil {
    39  		return err
    40  	}
    41  	if rule != nil && s.srv.config.ACLEnforceVersion8 {
    42  		switch args.Op {
    43  		case structs.SessionDestroy:
    44  			state := s.srv.fsm.State()
    45  			_, existing, err := state.SessionGet(nil, args.Session.ID)
    46  			if err != nil {
    47  				return fmt.Errorf("Session lookup failed: %v", err)
    48  			}
    49  			if existing == nil {
    50  				return fmt.Errorf("Unknown session %q", args.Session.ID)
    51  			}
    52  			if !rule.SessionWrite(existing.Node) {
    53  				return acl.ErrPermissionDenied
    54  			}
    55  
    56  		case structs.SessionCreate:
    57  			if !rule.SessionWrite(args.Session.Node) {
    58  				return acl.ErrPermissionDenied
    59  			}
    60  
    61  		default:
    62  			return fmt.Errorf("Invalid session operation %q", args.Op)
    63  		}
    64  	}
    65  
    66  	// Ensure that the specified behavior is allowed
    67  	switch args.Session.Behavior {
    68  	case "":
    69  		// Default behavior to Release for backwards compatibility
    70  		args.Session.Behavior = structs.SessionKeysRelease
    71  	case structs.SessionKeysRelease:
    72  	case structs.SessionKeysDelete:
    73  	default:
    74  		return fmt.Errorf("Invalid Behavior setting '%s'", args.Session.Behavior)
    75  	}
    76  
    77  	// Ensure the Session TTL is valid if provided
    78  	if args.Session.TTL != "" {
    79  		ttl, err := time.ParseDuration(args.Session.TTL)
    80  		if err != nil {
    81  			return fmt.Errorf("Session TTL '%s' invalid: %v", args.Session.TTL, err)
    82  		}
    83  
    84  		if ttl != 0 && (ttl < s.srv.config.SessionTTLMin || ttl > structs.SessionTTLMax) {
    85  			return fmt.Errorf("Invalid Session TTL '%d', must be between [%v=%v]",
    86  				ttl, s.srv.config.SessionTTLMin, structs.SessionTTLMax)
    87  		}
    88  	}
    89  
    90  	// If this is a create, we must generate the Session ID. This must
    91  	// be done prior to appending to the raft log, because the ID is not
    92  	// deterministic. Once the entry is in the log, the state update MUST
    93  	// be deterministic or the followers will not converge.
    94  	if args.Op == structs.SessionCreate {
    95  		// Generate a new session ID, verify uniqueness
    96  		state := s.srv.fsm.State()
    97  		for {
    98  			var err error
    99  			if args.Session.ID, err = uuid.GenerateUUID(); err != nil {
   100  				s.srv.logger.Printf("[ERR] consul.session: UUID generation failed: %v", err)
   101  				return err
   102  			}
   103  			_, sess, err := state.SessionGet(nil, args.Session.ID)
   104  			if err != nil {
   105  				s.srv.logger.Printf("[ERR] consul.session: Session lookup failed: %v", err)
   106  				return err
   107  			}
   108  			if sess == nil {
   109  				break
   110  			}
   111  		}
   112  	}
   113  
   114  	// Apply the update
   115  	resp, err := s.srv.raftApply(structs.SessionRequestType, args)
   116  	if err != nil {
   117  		s.srv.logger.Printf("[ERR] consul.session: Apply failed: %v", err)
   118  		return err
   119  	}
   120  
   121  	if args.Op == structs.SessionCreate && args.Session.TTL != "" {
   122  		// If we created a session with a TTL, reset the expiration timer
   123  		s.srv.resetSessionTimer(args.Session.ID, &args.Session)
   124  	} else if args.Op == structs.SessionDestroy {
   125  		// If we destroyed a session, it might potentially have a TTL,
   126  		// and we need to clear the timer
   127  		s.srv.clearSessionTimer(args.Session.ID)
   128  	}
   129  
   130  	if respErr, ok := resp.(error); ok {
   131  		return respErr
   132  	}
   133  
   134  	// Check if the return type is a string
   135  	if respString, ok := resp.(string); ok {
   136  		*reply = respString
   137  	}
   138  	return nil
   139  }
   140  
   141  // Get is used to retrieve a single session
   142  func (s *Session) Get(args *structs.SessionSpecificRequest,
   143  	reply *structs.IndexedSessions) error {
   144  	if done, err := s.srv.forward("Session.Get", args, args, reply); done {
   145  		return err
   146  	}
   147  
   148  	return s.srv.blockingQuery(
   149  		&args.QueryOptions,
   150  		&reply.QueryMeta,
   151  		func(ws memdb.WatchSet, state *state.Store) error {
   152  			index, session, err := state.SessionGet(ws, args.Session)
   153  			if err != nil {
   154  				return err
   155  			}
   156  
   157  			reply.Index = index
   158  			if session != nil {
   159  				reply.Sessions = structs.Sessions{session}
   160  			} else {
   161  				reply.Sessions = nil
   162  			}
   163  			if err := s.srv.filterACL(args.Token, reply); err != nil {
   164  				return err
   165  			}
   166  			return nil
   167  		})
   168  }
   169  
   170  // List is used to list all the active sessions
   171  func (s *Session) List(args *structs.DCSpecificRequest,
   172  	reply *structs.IndexedSessions) error {
   173  	if done, err := s.srv.forward("Session.List", args, args, reply); done {
   174  		return err
   175  	}
   176  
   177  	return s.srv.blockingQuery(
   178  		&args.QueryOptions,
   179  		&reply.QueryMeta,
   180  		func(ws memdb.WatchSet, state *state.Store) error {
   181  			index, sessions, err := state.SessionList(ws)
   182  			if err != nil {
   183  				return err
   184  			}
   185  
   186  			reply.Index, reply.Sessions = index, sessions
   187  			if err := s.srv.filterACL(args.Token, reply); err != nil {
   188  				return err
   189  			}
   190  			return nil
   191  		})
   192  }
   193  
   194  // NodeSessions is used to get all the sessions for a particular node
   195  func (s *Session) NodeSessions(args *structs.NodeSpecificRequest,
   196  	reply *structs.IndexedSessions) error {
   197  	if done, err := s.srv.forward("Session.NodeSessions", args, args, reply); done {
   198  		return err
   199  	}
   200  
   201  	return s.srv.blockingQuery(
   202  		&args.QueryOptions,
   203  		&reply.QueryMeta,
   204  		func(ws memdb.WatchSet, state *state.Store) error {
   205  			index, sessions, err := state.NodeSessions(ws, args.Node)
   206  			if err != nil {
   207  				return err
   208  			}
   209  
   210  			reply.Index, reply.Sessions = index, sessions
   211  			if err := s.srv.filterACL(args.Token, reply); err != nil {
   212  				return err
   213  			}
   214  			return nil
   215  		})
   216  }
   217  
   218  // Renew is used to renew the TTL on a single session
   219  func (s *Session) Renew(args *structs.SessionSpecificRequest,
   220  	reply *structs.IndexedSessions) error {
   221  	if done, err := s.srv.forward("Session.Renew", args, args, reply); done {
   222  		return err
   223  	}
   224  	defer metrics.MeasureSince([]string{"session", "renew"}, time.Now())
   225  
   226  	// Get the session, from local state.
   227  	state := s.srv.fsm.State()
   228  	index, session, err := state.SessionGet(nil, args.Session)
   229  	if err != nil {
   230  		return err
   231  	}
   232  
   233  	reply.Index = index
   234  	if session == nil {
   235  		return nil
   236  	}
   237  
   238  	// Fetch the ACL token, if any, and apply the policy.
   239  	rule, err := s.srv.ResolveToken(args.Token)
   240  	if err != nil {
   241  		return err
   242  	}
   243  	if rule != nil && s.srv.config.ACLEnforceVersion8 {
   244  		if !rule.SessionWrite(session.Node) {
   245  			return acl.ErrPermissionDenied
   246  		}
   247  	}
   248  
   249  	// Reset the session TTL timer.
   250  	reply.Sessions = structs.Sessions{session}
   251  	if err := s.srv.resetSessionTimer(args.Session, session); err != nil {
   252  		s.srv.logger.Printf("[ERR] consul.session: Session renew failed: %v", err)
   253  		return err
   254  	}
   255  
   256  	return nil
   257  }