// sessionCreateTxn is the inner method used for creating session entries in // an open transaction. Any health checks registered with the session will be // checked for failing status. Returns any error encountered. func (s *StateStore) sessionCreateTxn(tx *memdb.Txn, idx uint64, sess *structs.Session) error { // Check that we have a session ID if sess.ID == "" { return ErrMissingSessionID } // Verify the session behavior is valid switch sess.Behavior { case "": // Release by default to preserve backwards compatibility sess.Behavior = structs.SessionKeysRelease case structs.SessionKeysRelease: case structs.SessionKeysDelete: default: return fmt.Errorf("Invalid session behavior: %s", sess.Behavior) } // Assign the indexes. ModifyIndex likely will not be used but // we set it here anyways for sanity. sess.CreateIndex = idx sess.ModifyIndex = idx // Check that the node exists node, err := tx.First("nodes", "id", sess.Node) if err != nil { return fmt.Errorf("failed node lookup: %s", err) } if node == nil { return ErrMissingNode } // Go over the session checks and ensure they exist. for _, checkID := range sess.Checks { check, err := tx.First("checks", "id", sess.Node, string(checkID)) if err != nil { return fmt.Errorf("failed check lookup: %s", err) } if check == nil { return fmt.Errorf("Missing check '%s' registration", checkID) } // Check that the check is not in critical state status := check.(*structs.HealthCheck).Status if status == structs.HealthCritical { return fmt.Errorf("Check '%s' is in %s state", checkID, status) } } // Insert the session if err := tx.Insert("sessions", sess); err != nil { return fmt.Errorf("failed inserting session: %s", err) } // Insert the check mappings for _, checkID := range sess.Checks { mapping := &sessionCheck{ Node: sess.Node, CheckID: checkID, Session: sess.ID, } if err := tx.Insert("session_checks", mapping); err != nil { return fmt.Errorf("failed inserting session check mapping: %s", err) } } // Update the index if err := tx.Insert("index", &IndexEntry{"sessions", idx}); err != nil { return fmt.Errorf("failed updating index: %s", err) } tx.Defer(func() { s.tableWatches["sessions"].Notify() }) return nil }
// SessionCreate is used to create a new session. The // ID will be populated on a successful return func (s *StateStore) SessionCreate(index uint64, session *structs.Session) error { // Assign the create index session.CreateIndex = index // Start the transaction tx, err := s.tables.StartTxn(false) if err != nil { panic(fmt.Errorf("Failed to start txn: %v", err)) } defer tx.Abort() // Verify that the node exists res, err := s.nodeTable.GetTxn(tx, "id", session.Node) if err != nil { return err } if len(res) == 0 { return fmt.Errorf("Missing node registration") } // Verify that the checks exist and are not critical for _, checkId := range session.Checks { res, err := s.checkTable.GetTxn(tx, "id", session.Node, checkId) if err != nil { return err } if len(res) == 0 { return fmt.Errorf("Missing check '%s' registration", checkId) } chk := res[0].(*structs.HealthCheck) if chk.Status == structs.HealthCritical { return fmt.Errorf("Check '%s' is in %s state", checkId, chk.Status) } } // Generate a new session ID, verify uniqueness session.ID = generateUUID() for { res, err = s.sessionTable.GetTxn(tx, "id", session.ID) if err != nil { return err } // Quit if this ID is unique if len(res) == 0 { break } } // Insert the session if err := s.sessionTable.InsertTxn(tx, session); err != nil { return err } // Insert the check mappings sCheck := sessionCheck{Node: session.Node, Session: session.ID} for _, checkID := range session.Checks { sCheck.CheckID = checkID if err := s.sessionCheckTable.InsertTxn(tx, &sCheck); err != nil { return err } } // Trigger the update notifications if err := s.sessionTable.SetLastIndexTxn(tx, index); err != nil { return err } tx.Defer(func() { s.watch[s.sessionTable].Notify() }) return tx.Commit() }