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

     1  package raftutil
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/hashicorp/go-msgpack/codec"
    10  	"github.com/hashicorp/nomad/nomad/structs"
    11  	"github.com/hashicorp/raft"
    12  	raftboltdb "github.com/hashicorp/raft-boltdb"
    13  )
    14  
    15  // RaftStateInfo returns info about the nomad state, as found in the passed data-dir directory
    16  func RaftStateInfo(p string) (store *raftboltdb.BoltStore, firstIdx uint64, lastIdx uint64, err error) {
    17  	s, err := raftboltdb.NewBoltStore(p)
    18  	if err != nil {
    19  		return nil, 0, 0, fmt.Errorf("failed to open raft logs: %v", err)
    20  	}
    21  
    22  	firstIdx, err = s.FirstIndex()
    23  	if err != nil {
    24  		return nil, 0, 0, fmt.Errorf("failed to fetch first index: %v", err)
    25  	}
    26  
    27  	lastIdx, err = s.LastIndex()
    28  	if err != nil {
    29  		return nil, 0, 0, fmt.Errorf("failed to fetch last index: %v", err)
    30  	}
    31  
    32  	return s, firstIdx, lastIdx, nil
    33  }
    34  
    35  // LogEntries returns the log entries as found in raft log in the passed data-dir directory
    36  func LogEntries(p string) (logs []interface{}, warnings []error, err error) {
    37  	store, firstIdx, lastIdx, err := RaftStateInfo(p)
    38  	if err != nil {
    39  		return nil, nil, fmt.Errorf("failed to open raft logs: %v", err)
    40  	}
    41  	defer store.Close()
    42  
    43  	result := make([]interface{}, 0, lastIdx-firstIdx+1)
    44  	for i := firstIdx; i <= lastIdx; i++ {
    45  		var e raft.Log
    46  		err := store.GetLog(i, &e)
    47  		if err != nil {
    48  			warnings = append(warnings, fmt.Errorf("failed to read log entry at index %d (firstIdx: %d, lastIdx: %d): %v", i, firstIdx, lastIdx, err))
    49  			continue
    50  		}
    51  
    52  		m, err := decode(&e)
    53  		if err != nil {
    54  			warnings = append(warnings, fmt.Errorf("failed to decode log entry at index %d: %v", i, err))
    55  			continue
    56  		}
    57  
    58  		result = append(result, m)
    59  	}
    60  
    61  	return result, warnings, nil
    62  }
    63  
    64  type logMessage struct {
    65  	LogType string
    66  	Term    uint64
    67  	Index   uint64
    68  
    69  	CommandType           string      `json:",omitempty"`
    70  	IgnoreUnknownTypeFlag bool        `json:",omitempty"`
    71  	Body                  interface{} `json:",omitempty"`
    72  }
    73  
    74  func decode(e *raft.Log) (*logMessage, error) {
    75  	m := &logMessage{
    76  		LogType: logTypes[e.Type],
    77  		Term:    e.Term,
    78  		Index:   e.Index,
    79  	}
    80  
    81  	if m.LogType == "" {
    82  		m.LogType = fmt.Sprintf("%d", e.Type)
    83  	}
    84  
    85  	var data []byte
    86  	if e.Type == raft.LogCommand {
    87  		if len(e.Data) == 0 {
    88  			return nil, fmt.Errorf("command did not include data")
    89  		}
    90  
    91  		msgType := structs.MessageType(e.Data[0])
    92  
    93  		m.CommandType = commandName(msgType & ^structs.IgnoreUnknownTypeFlag)
    94  		m.IgnoreUnknownTypeFlag = (msgType & structs.IgnoreUnknownTypeFlag) != 0
    95  
    96  		data = e.Data[1:]
    97  	} else {
    98  		data = e.Data
    99  	}
   100  
   101  	if len(data) != 0 {
   102  		decoder := codec.NewDecoder(bytes.NewReader(data), structs.MsgpackHandle)
   103  
   104  		var v interface{}
   105  		var err error
   106  		if m.CommandType == commandName(structs.JobBatchDeregisterRequestType) {
   107  			var vr structs.JobBatchDeregisterRequest
   108  			err = decoder.Decode(&vr)
   109  			v = jsonifyJobBatchDeregisterRequest(&vr)
   110  		} else {
   111  			var vr interface{}
   112  			err = decoder.Decode(&vr)
   113  			v = vr
   114  		}
   115  
   116  		if err != nil {
   117  			fmt.Fprintf(os.Stderr, "failed to decode log entry at index %d: failed to decode body of %v.%v %v\n", e.Index, e.Type, m.CommandType, err)
   118  			v = "FAILED TO DECODE DATA"
   119  		}
   120  		fixTime(v)
   121  		m.Body = v
   122  	}
   123  
   124  	return m, nil
   125  }
   126  
   127  // jsonifyJobBatchDeregisterRequest special case JsonBatchDeregisterRequest object
   128  // as the actual type is not json friendly.
   129  func jsonifyJobBatchDeregisterRequest(v *structs.JobBatchDeregisterRequest) interface{} {
   130  	var data struct {
   131  		Jobs  map[string]*structs.JobDeregisterOptions
   132  		Evals []*structs.Evaluation
   133  		structs.WriteRequest
   134  	}
   135  	data.Evals = v.Evals
   136  	data.WriteRequest = v.WriteRequest
   137  
   138  	data.Jobs = make(map[string]*structs.JobDeregisterOptions, len(v.Jobs))
   139  	if len(v.Jobs) != 0 {
   140  		for k, v := range v.Jobs {
   141  			data.Jobs[k.Namespace+"."+k.ID] = v
   142  		}
   143  	}
   144  	return data
   145  }
   146  
   147  var logTypes = map[raft.LogType]string{
   148  	raft.LogCommand:              "LogCommand",
   149  	raft.LogNoop:                 "LogNoop",
   150  	raft.LogAddPeerDeprecated:    "LogAddPeerDeprecated",
   151  	raft.LogRemovePeerDeprecated: "LogRemovePeerDeprecated",
   152  	raft.LogBarrier:              "LogBarrier",
   153  	raft.LogConfiguration:        "LogConfiguration",
   154  }
   155  
   156  func commandName(mt structs.MessageType) string {
   157  	n := msgTypeNames[mt]
   158  	if n != "" {
   159  		return n
   160  	}
   161  
   162  	return fmt.Sprintf("%v", mt)
   163  }
   164  
   165  // FindRaftFile finds raft.db and returns path
   166  func FindRaftFile(p string) (raftpath string, err error) {
   167  	// Try known locations before traversal to avoid walking deep structure
   168  	if _, err = os.Stat(filepath.Join(p, "server", "raft", "raft.db")); err == nil {
   169  		raftpath = filepath.Join(p, "server", "raft", "raft.db")
   170  	} else if _, err = os.Stat(filepath.Join(p, "raft", "raft.db")); err == nil {
   171  		raftpath = filepath.Join(p, "raft", "raft.db")
   172  	} else if _, err = os.Stat(filepath.Join(p, "raft.db")); err == nil {
   173  		raftpath = filepath.Join(p, "raft.db")
   174  	} else if _, err = os.Stat(p); err == nil && filepath.Ext(p) == ".db" {
   175  		// Also accept path to .db file
   176  		raftpath = p
   177  	} else {
   178  		raftpath, err = FindFileInPath("raft.db", p)
   179  	}
   180  
   181  	if err != nil {
   182  		return "", err
   183  	}
   184  
   185  	return raftpath, nil
   186  }
   187  
   188  // FindRaftDir finds raft.db and returns parent directory path
   189  func FindRaftDir(p string) (raftpath string, err error) {
   190  	raftpath, err = FindRaftFile(p)
   191  	if err != nil {
   192  		return "", err
   193  	}
   194  
   195  	if raftpath == "" {
   196  		return "", fmt.Errorf("failed to find raft dir in %s", p)
   197  	}
   198  
   199  	return filepath.Dir(raftpath), nil
   200  }
   201  
   202  // FindFileInPath searches for file in path p
   203  func FindFileInPath(file string, p string) (path string, err error) {
   204  	// Define walk function to find file
   205  	walkFn := func(walkPath string, info os.FileInfo, err error) error {
   206  		if err != nil {
   207  			return err
   208  		}
   209  
   210  		if filepath.Base(walkPath) == file {
   211  			path = walkPath
   212  		}
   213  		return nil
   214  	}
   215  
   216  	// Walk path p looking for file
   217  	walkErr := filepath.Walk(p, walkFn)
   218  	if walkErr != nil {
   219  		return "", walkErr
   220  	}
   221  
   222  	if path == "" {
   223  		return "", fmt.Errorf("File %s not found in path %s", file, p)
   224  	}
   225  
   226  	return path, nil
   227  }