github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/raft/storage/walwrap.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"sort"
    10  	"strings"
    11  
    12  	"github.com/coreos/etcd/raft/raftpb"
    13  	"github.com/coreos/etcd/wal"
    14  	"github.com/coreos/etcd/wal/walpb"
    15  	"github.com/docker/swarmkit/log"
    16  	"github.com/docker/swarmkit/manager/encryption"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  // This package wraps the github.com/coreos/etcd/wal package, and encrypts
    21  // the bytes of whatever entry is passed to it, and decrypts the bytes of
    22  // whatever entry it reads.
    23  
    24  // WAL is the interface presented by github.com/coreos/etcd/wal.WAL that we depend upon
    25  type WAL interface {
    26  	ReadAll() ([]byte, raftpb.HardState, []raftpb.Entry, error)
    27  	ReleaseLockTo(index uint64) error
    28  	Close() error
    29  	Save(st raftpb.HardState, ents []raftpb.Entry) error
    30  	SaveSnapshot(e walpb.Snapshot) error
    31  }
    32  
    33  // WALFactory provides an interface for the different ways to get a WAL object.
    34  // For instance, the etcd/wal package itself provides this
    35  type WALFactory interface {
    36  	Create(dirpath string, metadata []byte) (WAL, error)
    37  	Open(dirpath string, walsnap walpb.Snapshot) (WAL, error)
    38  }
    39  
    40  var _ WAL = &wrappedWAL{}
    41  var _ WAL = &wal.WAL{}
    42  var _ WALFactory = walCryptor{}
    43  
    44  // wrappedWAL wraps a github.com/coreos/etcd/wal.WAL, and handles encrypting/decrypting
    45  type wrappedWAL struct {
    46  	*wal.WAL
    47  	encrypter encryption.Encrypter
    48  	decrypter encryption.Decrypter
    49  }
    50  
    51  // ReadAll wraps the wal.WAL.ReadAll() function, but it first checks to see if the
    52  // metadata indicates that the entries are encryptd, and if so, decrypts them.
    53  func (w *wrappedWAL) ReadAll() ([]byte, raftpb.HardState, []raftpb.Entry, error) {
    54  	metadata, state, ents, err := w.WAL.ReadAll()
    55  	if err != nil {
    56  		return metadata, state, ents, err
    57  	}
    58  	for i, ent := range ents {
    59  		ents[i].Data, err = encryption.Decrypt(ent.Data, w.decrypter)
    60  		if err != nil {
    61  			return nil, raftpb.HardState{}, nil, err
    62  		}
    63  	}
    64  
    65  	return metadata, state, ents, nil
    66  }
    67  
    68  // Save encrypts the entry data (if an encrypter is exists) before passing it onto the
    69  // wrapped wal.WAL's Save function.
    70  func (w *wrappedWAL) Save(st raftpb.HardState, ents []raftpb.Entry) error {
    71  	var writeEnts []raftpb.Entry
    72  	for _, ent := range ents {
    73  		data, err := encryption.Encrypt(ent.Data, w.encrypter)
    74  		if err != nil {
    75  			return err
    76  		}
    77  		writeEnts = append(writeEnts, raftpb.Entry{
    78  			Index: ent.Index,
    79  			Term:  ent.Term,
    80  			Type:  ent.Type,
    81  			Data:  data,
    82  		})
    83  	}
    84  
    85  	return w.WAL.Save(st, writeEnts)
    86  }
    87  
    88  // walCryptor is an object that provides the same functions as `etcd/wal`
    89  // and `etcd/snap` that we need to open a WAL object or Snapshotter object
    90  type walCryptor struct {
    91  	encrypter encryption.Encrypter
    92  	decrypter encryption.Decrypter
    93  }
    94  
    95  // NewWALFactory returns an object that can be used to produce objects that
    96  // will read from and write to encrypted WALs on disk.
    97  func NewWALFactory(encrypter encryption.Encrypter, decrypter encryption.Decrypter) WALFactory {
    98  	return walCryptor{
    99  		encrypter: encrypter,
   100  		decrypter: decrypter,
   101  	}
   102  }
   103  
   104  // Create returns a new WAL object with the given encrypters and decrypters.
   105  func (wc walCryptor) Create(dirpath string, metadata []byte) (WAL, error) {
   106  	w, err := wal.Create(dirpath, metadata)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	return &wrappedWAL{
   111  		WAL:       w,
   112  		encrypter: wc.encrypter,
   113  		decrypter: wc.decrypter,
   114  	}, nil
   115  }
   116  
   117  // Open returns a new WAL object with the given encrypters and decrypters.
   118  func (wc walCryptor) Open(dirpath string, snap walpb.Snapshot) (WAL, error) {
   119  	w, err := wal.Open(dirpath, snap)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	return &wrappedWAL{
   124  		WAL:       w,
   125  		encrypter: wc.encrypter,
   126  		decrypter: wc.decrypter,
   127  	}, nil
   128  }
   129  
   130  type originalWAL struct{}
   131  
   132  func (o originalWAL) Create(dirpath string, metadata []byte) (WAL, error) {
   133  	return wal.Create(dirpath, metadata)
   134  }
   135  func (o originalWAL) Open(dirpath string, walsnap walpb.Snapshot) (WAL, error) {
   136  	return wal.Open(dirpath, walsnap)
   137  }
   138  
   139  // OriginalWAL is the original `wal` package as an implementation of the WALFactory interface
   140  var OriginalWAL WALFactory = originalWAL{}
   141  
   142  // WALData contains all the data returned by a WAL's ReadAll() function
   143  // (metadata, hardwate, and entries)
   144  type WALData struct {
   145  	Metadata  []byte
   146  	HardState raftpb.HardState
   147  	Entries   []raftpb.Entry
   148  }
   149  
   150  // ReadRepairWAL opens a WAL for reading, and attempts to read it.  If we can't read it, attempts to repair
   151  // and read again.
   152  func ReadRepairWAL(
   153  	ctx context.Context,
   154  	walDir string,
   155  	walsnap walpb.Snapshot,
   156  	factory WALFactory,
   157  ) (WAL, WALData, error) {
   158  	var (
   159  		reader   WAL
   160  		metadata []byte
   161  		st       raftpb.HardState
   162  		ents     []raftpb.Entry
   163  		err      error
   164  	)
   165  	repaired := false
   166  	for {
   167  		if reader, err = factory.Open(walDir, walsnap); err != nil {
   168  			return nil, WALData{}, errors.Wrap(err, "failed to open WAL")
   169  		}
   170  		if metadata, st, ents, err = reader.ReadAll(); err != nil {
   171  			if closeErr := reader.Close(); closeErr != nil {
   172  				return nil, WALData{}, closeErr
   173  			}
   174  			if _, ok := err.(encryption.ErrCannotDecrypt); ok {
   175  				return nil, WALData{}, errors.Wrap(err, "failed to decrypt WAL")
   176  			}
   177  			// we can only repair ErrUnexpectedEOF and we never repair twice.
   178  			if repaired || err != io.ErrUnexpectedEOF {
   179  				return nil, WALData{}, errors.Wrap(err, "irreparable WAL error")
   180  			}
   181  			if !wal.Repair(walDir) {
   182  				return nil, WALData{}, errors.Wrap(err, "WAL error cannot be repaired")
   183  			}
   184  			log.G(ctx).WithError(err).Info("repaired WAL error")
   185  			repaired = true
   186  			continue
   187  		}
   188  		break
   189  	}
   190  	return reader, WALData{
   191  		Metadata:  metadata,
   192  		HardState: st,
   193  		Entries:   ents,
   194  	}, nil
   195  }
   196  
   197  // MigrateWALs reads existing WALs (from a particular snapshot and beyond) from one directory, encoded one way,
   198  // and writes them to a new directory, encoded a different way
   199  func MigrateWALs(ctx context.Context, oldDir, newDir string, oldFactory, newFactory WALFactory, snapshot walpb.Snapshot) error {
   200  	oldReader, waldata, err := ReadRepairWAL(ctx, oldDir, snapshot, oldFactory)
   201  	if err != nil {
   202  		return err
   203  	}
   204  	oldReader.Close()
   205  
   206  	if err := os.MkdirAll(filepath.Dir(newDir), 0700); err != nil {
   207  		return errors.Wrap(err, "could not create parent directory")
   208  	}
   209  
   210  	// keep temporary wal directory so WAL initialization appears atomic
   211  	tmpdirpath := filepath.Clean(newDir) + ".tmp"
   212  	if err := os.RemoveAll(tmpdirpath); err != nil {
   213  		return errors.Wrap(err, "could not remove temporary WAL directory")
   214  	}
   215  	defer os.RemoveAll(tmpdirpath)
   216  
   217  	tmpWAL, err := newFactory.Create(tmpdirpath, waldata.Metadata)
   218  	if err != nil {
   219  		return errors.Wrap(err, "could not create new WAL in temporary WAL directory")
   220  	}
   221  	defer tmpWAL.Close()
   222  
   223  	if err := tmpWAL.SaveSnapshot(snapshot); err != nil {
   224  		return errors.Wrap(err, "could not write WAL snapshot in temporary directory")
   225  	}
   226  
   227  	if err := tmpWAL.Save(waldata.HardState, waldata.Entries); err != nil {
   228  		return errors.Wrap(err, "could not migrate WALs to temporary directory")
   229  	}
   230  	if err := tmpWAL.Close(); err != nil {
   231  		return err
   232  	}
   233  
   234  	return os.Rename(tmpdirpath, newDir)
   235  }
   236  
   237  // ListWALs lists all the wals in a directory and returns the list in lexical
   238  // order (oldest first)
   239  func ListWALs(dirpath string) ([]string, error) {
   240  	dirents, err := ioutil.ReadDir(dirpath)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	var wals []string
   246  	for _, dirent := range dirents {
   247  		if strings.HasSuffix(dirent.Name(), ".wal") {
   248  			wals = append(wals, dirent.Name())
   249  		}
   250  	}
   251  
   252  	// Sort WAL filenames in lexical order
   253  	sort.Sort(sort.StringSlice(wals))
   254  	return wals, nil
   255  }