github.com/Aestek/consul@v1.2.4-0.20190309222502-b2c31e33971a/agent/proxyprocess/snapshot.go (about)

     1  package proxyprocess
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  
    11  	"github.com/hashicorp/consul/agent/structs"
    12  	"github.com/hashicorp/consul/lib/file"
    13  )
    14  
    15  // snapshot is the structure of the snapshot file. This is unexported because
    16  // we don't want this being a public API.
    17  //
    18  // The snapshot doesn't contain any configuration for the manager. We only
    19  // want to restore the proxies that we're managing, and we use the config
    20  // set at runtime to sync and reconcile what proxies we should start,
    21  // restart, stop, or have already running.
    22  type snapshot struct {
    23  	// Version is the version of the snapshot format and can be used
    24  	// to safely update the format in the future if necessary.
    25  	Version int
    26  
    27  	// Proxies are the set of proxies that the manager has.
    28  	Proxies map[string]snapshotProxy
    29  }
    30  
    31  // snapshotProxy represents a single proxy.
    32  type snapshotProxy struct {
    33  	// Mode corresponds to the type of proxy running.
    34  	Mode structs.ProxyExecMode
    35  
    36  	// Config is an opaque mapping of primitive values that the proxy
    37  	// implementation uses to restore state.
    38  	Config map[string]interface{}
    39  }
    40  
    41  // snapshotVersion is the current version to encode within the snapshot.
    42  const snapshotVersion = 1
    43  
    44  // SnapshotPath returns the default snapshot path for this manager. This
    45  // will return empty if DataDir is not set. This file may not exist yet.
    46  func (m *Manager) SnapshotPath() string {
    47  	if m.DataDir == "" {
    48  		return ""
    49  	}
    50  
    51  	return filepath.Join(m.DataDir, "snapshot.json")
    52  }
    53  
    54  // Snapshot will persist a snapshot of the proxy manager state that
    55  // can be restored with Restore.
    56  //
    57  // If DataDir is non-empty, then the Manager will automatically snapshot
    58  // whenever the set of managed proxies changes. This method generally doesn't
    59  // need to be called manually.
    60  func (m *Manager) Snapshot(path string) error {
    61  	m.lock.Lock()
    62  	defer m.lock.Unlock()
    63  	return m.snapshot(path, false)
    64  }
    65  
    66  // snapshot is the internal function analogous to Snapshot but expects
    67  // a lock to already be held.
    68  //
    69  // checkDup when set will store the snapshot on lastSnapshot and use
    70  // reflect.DeepEqual to verify that its not writing an identical snapshot.
    71  func (m *Manager) snapshot(path string, checkDup bool) error {
    72  	// Build the snapshot
    73  	s := snapshot{
    74  		Version: snapshotVersion,
    75  		Proxies: make(map[string]snapshotProxy, len(m.proxies)),
    76  	}
    77  	for id, p := range m.proxies {
    78  		// Get the snapshot configuration. If the configuration is nil or
    79  		// empty then we don't persist this proxy.
    80  		config := p.MarshalSnapshot()
    81  		if len(config) == 0 {
    82  			continue
    83  		}
    84  
    85  		s.Proxies[id] = snapshotProxy{
    86  			Mode:   proxyExecMode(p),
    87  			Config: config,
    88  		}
    89  	}
    90  
    91  	// Dup detection, if the snapshot is identical to the last, do nothing
    92  	if checkDup && reflect.DeepEqual(m.lastSnapshot, &s) {
    93  		return nil
    94  	}
    95  
    96  	// Encode as JSON
    97  	encoded, err := json.Marshal(&s)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	// Write the file
   103  	err = file.WriteAtomic(path, encoded)
   104  
   105  	// If we are checking for dups and we had a successful write, store
   106  	// it so we don't rewrite the same value.
   107  	if checkDup && err == nil {
   108  		m.lastSnapshot = &s
   109  	}
   110  	return err
   111  }
   112  
   113  // Restore restores the manager state from a snapshot at path. If path
   114  // doesn't exist, this does nothing and no error is returned.
   115  //
   116  // This restores proxy state but does not restore any Manager configuration
   117  // such as DataDir, Logger, etc. All of those should be set _before_ Restore
   118  // is called.
   119  //
   120  // Restore must be called before Run. Restore will immediately start
   121  // supervising the restored processes but will not sync with the local
   122  // state store until Run is called.
   123  //
   124  // If an error is returned the manager state is left untouched.
   125  func (m *Manager) Restore(path string) error {
   126  	buf, err := ioutil.ReadFile(path)
   127  	if err != nil {
   128  		if os.IsNotExist(err) {
   129  			return nil
   130  		}
   131  
   132  		return err
   133  	}
   134  
   135  	var s snapshot
   136  	if err := json.Unmarshal(buf, &s); err != nil {
   137  		return err
   138  	}
   139  
   140  	// Verify the version matches so we can be more confident that we're
   141  	// decoding a structure that we expect.
   142  	if s.Version != snapshotVersion {
   143  		return fmt.Errorf("unknown snapshot version, expecting %d", snapshotVersion)
   144  	}
   145  
   146  	// Build the proxies from the snapshot
   147  	proxies := make(map[string]Proxy, len(s.Proxies))
   148  	for id, sp := range s.Proxies {
   149  		p, err := m.newProxyFromMode(sp.Mode, id)
   150  		if err != nil {
   151  			return err
   152  		}
   153  
   154  		// Unmarshal the proxy. If there is an error we just continue on and
   155  		// ignore it. Errors restoring proxies should be exceptionally rare
   156  		// and only under scenarios where the proxy isn't running anymore or
   157  		// we won't have permission to access it. We log and continue.
   158  		if err := p.UnmarshalSnapshot(sp.Config); err != nil {
   159  			m.Logger.Printf("[WARN] agent/proxy: error restoring proxy %q: %s", id, err)
   160  			continue
   161  		}
   162  
   163  		proxies[id] = p
   164  	}
   165  
   166  	// Overwrite the proxies. The documentation notes that this will happen.
   167  	m.lock.Lock()
   168  	defer m.lock.Unlock()
   169  	m.proxies = proxies
   170  	return nil
   171  }