github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/helper/raftutil/fsm.go (about)

     1  package raftutil
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/hashicorp/go-hclog"
     9  	"github.com/hashicorp/go-memdb"
    10  	"github.com/hashicorp/nomad/nomad"
    11  	"github.com/hashicorp/raft"
    12  )
    13  
    14  // FSMState returns a dump of the FSM state as found in data-dir, as of lastIndx value
    15  func FSMState(p string, plastIdx int64) (interface{}, error) {
    16  	store, firstIdx, lastIdx, err := RaftStateInfo(filepath.Join(p, "raft.db"))
    17  	if err != nil {
    18  		return nil, fmt.Errorf("failed to open raft database %v: %v", p, err)
    19  	}
    20  	defer store.Close()
    21  
    22  	snaps, err := raft.NewFileSnapshotStore(p, 1000, os.Stderr)
    23  	if err != nil {
    24  		return nil, fmt.Errorf("failed to open snapshot dir: %v", err)
    25  	}
    26  
    27  	logger := hclog.L()
    28  
    29  	// use dummy non-enabled FSM dependencies
    30  	periodicDispatch := nomad.NewPeriodicDispatch(logger, nil)
    31  	blockedEvals := nomad.NewBlockedEvals(nil, logger)
    32  	evalBroker, err := nomad.NewEvalBroker(1, 1, 1, 1)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	fsmConfig := &nomad.FSMConfig{
    37  		EvalBroker: evalBroker,
    38  		Periodic:   periodicDispatch,
    39  		Blocked:    blockedEvals,
    40  		Logger:     logger,
    41  		Region:     "default",
    42  	}
    43  
    44  	fsm, err := nomad.NewFSM(fsmConfig)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	// restore from snapshot first
    50  	sFirstIdx, err := restoreFromSnapshot(fsm, snaps, logger)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	if sFirstIdx+1 < firstIdx {
    56  		return nil, fmt.Errorf("missing logs after snapshot [%v,%v]", sFirstIdx+1, firstIdx-1)
    57  	} else if sFirstIdx > 0 {
    58  		firstIdx = sFirstIdx + 1
    59  	}
    60  
    61  	lastIdx = lastIndex(lastIdx, plastIdx)
    62  
    63  	for i := firstIdx; i <= lastIdx; i++ {
    64  		var e raft.Log
    65  		err := store.GetLog(i, &e)
    66  		if err != nil {
    67  			return nil, fmt.Errorf("failed to read log entry at index %d: %v, firstIdx: %d, lastIdx: %d", i, err, firstIdx, lastIdx)
    68  		}
    69  
    70  		if e.Type == raft.LogCommand {
    71  			fsm.Apply(&e)
    72  		}
    73  	}
    74  
    75  	state := fsm.State()
    76  	result := map[string][]interface{}{
    77  		"ACLPolicies":      toArray(state.ACLPolicies(nil)),
    78  		"ACLTokens":        toArray(state.ACLTokens(nil)),
    79  		"Allocs":           toArray(state.Allocs(nil)),
    80  		"CSIPlugins":       toArray(state.CSIPlugins(nil)),
    81  		"CSIVolumes":       toArray(state.CSIVolumes(nil)),
    82  		"Deployments":      toArray(state.Deployments(nil)),
    83  		"Evals":            toArray(state.Evals(nil)),
    84  		"Indexes":          toArray(state.Indexes()),
    85  		"JobSummaries":     toArray(state.JobSummaries(nil)),
    86  		"JobVersions":      toArray(state.JobVersions(nil)),
    87  		"Jobs":             toArray(state.Jobs(nil)),
    88  		"Nodes":            toArray(state.Nodes(nil)),
    89  		"PeriodicLaunches": toArray(state.PeriodicLaunches(nil)),
    90  		"SITokenAccessors": toArray(state.SITokenAccessors(nil)),
    91  		"ScalingEvents":    toArray(state.ScalingEvents(nil)),
    92  		"ScalingPolicies":  toArray(state.ScalingPolicies(nil)),
    93  		"VaultAccessors":   toArray(state.VaultAccessors(nil)),
    94  	}
    95  
    96  	insertEnterpriseState(result, state)
    97  
    98  	return result, nil
    99  }
   100  
   101  func restoreFromSnapshot(fsm raft.FSM, snaps raft.SnapshotStore, logger hclog.Logger) (uint64, error) {
   102  	logger = logger.Named("restoreFromSnapshot")
   103  	snapshots, err := snaps.List()
   104  	if err != nil {
   105  		return 0, err
   106  	}
   107  	logger.Debug("found snapshots", "count", len(snapshots))
   108  
   109  	for _, snapshot := range snapshots {
   110  		_, source, err := snaps.Open(snapshot.ID)
   111  		if err != nil {
   112  			logger.Warn("failed to open a snapshot", "snapshot_id", snapshot.ID, "error", err)
   113  			continue
   114  		}
   115  
   116  		err = fsm.Restore(source)
   117  		source.Close()
   118  		if err != nil {
   119  			logger.Warn("failed to restore a snapshot", "snapshot_id", snapshot.ID, "error", err)
   120  			continue
   121  		}
   122  
   123  		return snapshot.Index, nil
   124  	}
   125  
   126  	return 0, nil
   127  }
   128  
   129  func lastIndex(raftLastIdx uint64, cliLastIdx int64) uint64 {
   130  	switch {
   131  	case cliLastIdx < 0:
   132  		if raftLastIdx > uint64(-cliLastIdx) {
   133  			return raftLastIdx - uint64(-cliLastIdx)
   134  		} else {
   135  			return 0
   136  		}
   137  	case cliLastIdx == 0:
   138  		return raftLastIdx
   139  	case uint64(cliLastIdx) < raftLastIdx:
   140  		return uint64(cliLastIdx)
   141  	default:
   142  		return raftLastIdx
   143  	}
   144  }
   145  
   146  func toArray(iter memdb.ResultIterator, err error) []interface{} {
   147  	if err != nil {
   148  		return []interface{}{err}
   149  	}
   150  
   151  	r := []interface{}{}
   152  
   153  	if iter != nil {
   154  		item := iter.Next()
   155  		for item != nil {
   156  			r = append(r, item)
   157  			item = iter.Next()
   158  		}
   159  	}
   160  
   161  	return r
   162  }