go.etcd.io/etcd@v3.3.27+incompatible/functional/agent/handler.go (about)

     1  // Copyright 2018 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package agent
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"net/url"
    22  	"os"
    23  	"os/exec"
    24  	"path/filepath"
    25  	"syscall"
    26  	"time"
    27  
    28  	"github.com/coreos/etcd/functional/rpcpb"
    29  	"github.com/coreos/etcd/pkg/fileutil"
    30  	"github.com/coreos/etcd/pkg/proxy"
    31  
    32  	"go.uber.org/zap"
    33  )
    34  
    35  // return error for system errors (e.g. fail to create files)
    36  // return status error in response for wrong configuration/operation (e.g. start etcd twice)
    37  func (srv *Server) handleTesterRequest(req *rpcpb.Request) (resp *rpcpb.Response, err error) {
    38  	defer func() {
    39  		if err == nil && req != nil {
    40  			srv.last = req.Operation
    41  			srv.lg.Info("handler success", zap.String("operation", req.Operation.String()))
    42  		}
    43  	}()
    44  	if req != nil {
    45  		srv.Member = req.Member
    46  		srv.Tester = req.Tester
    47  	}
    48  
    49  	switch req.Operation {
    50  	case rpcpb.Operation_INITIAL_START_ETCD:
    51  		return srv.handle_INITIAL_START_ETCD(req)
    52  	case rpcpb.Operation_RESTART_ETCD:
    53  		return srv.handle_RESTART_ETCD()
    54  
    55  	case rpcpb.Operation_SIGTERM_ETCD:
    56  		return srv.handle_SIGTERM_ETCD()
    57  	case rpcpb.Operation_SIGQUIT_ETCD_AND_REMOVE_DATA:
    58  		return srv.handle_SIGQUIT_ETCD_AND_REMOVE_DATA()
    59  
    60  	case rpcpb.Operation_SAVE_SNAPSHOT:
    61  		return srv.handle_SAVE_SNAPSHOT()
    62  	case rpcpb.Operation_RESTORE_RESTART_FROM_SNAPSHOT:
    63  		return srv.handle_RESTORE_RESTART_FROM_SNAPSHOT()
    64  	case rpcpb.Operation_RESTART_FROM_SNAPSHOT:
    65  		return srv.handle_RESTART_FROM_SNAPSHOT()
    66  
    67  	case rpcpb.Operation_SIGQUIT_ETCD_AND_ARCHIVE_DATA:
    68  		return srv.handle_SIGQUIT_ETCD_AND_ARCHIVE_DATA()
    69  	case rpcpb.Operation_SIGQUIT_ETCD_AND_REMOVE_DATA_AND_STOP_AGENT:
    70  		return srv.handle_SIGQUIT_ETCD_AND_REMOVE_DATA_AND_STOP_AGENT()
    71  
    72  	case rpcpb.Operation_BLACKHOLE_PEER_PORT_TX_RX:
    73  		return srv.handle_BLACKHOLE_PEER_PORT_TX_RX()
    74  	case rpcpb.Operation_UNBLACKHOLE_PEER_PORT_TX_RX:
    75  		return srv.handle_UNBLACKHOLE_PEER_PORT_TX_RX()
    76  	case rpcpb.Operation_DELAY_PEER_PORT_TX_RX:
    77  		return srv.handle_DELAY_PEER_PORT_TX_RX()
    78  	case rpcpb.Operation_UNDELAY_PEER_PORT_TX_RX:
    79  		return srv.handle_UNDELAY_PEER_PORT_TX_RX()
    80  
    81  	default:
    82  		msg := fmt.Sprintf("operation not found (%v)", req.Operation)
    83  		return &rpcpb.Response{Success: false, Status: msg}, errors.New(msg)
    84  	}
    85  }
    86  
    87  func (srv *Server) handle_INITIAL_START_ETCD(req *rpcpb.Request) (*rpcpb.Response, error) {
    88  	if srv.last != rpcpb.Operation_NOT_STARTED {
    89  		return &rpcpb.Response{
    90  			Success: false,
    91  			Status:  fmt.Sprintf("%q is not valid; last server operation was %q", rpcpb.Operation_INITIAL_START_ETCD.String(), srv.last.String()),
    92  			Member:  req.Member,
    93  		}, nil
    94  	}
    95  
    96  	err := fileutil.TouchDirAll(srv.Member.BaseDir)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	srv.lg.Info("created base directory", zap.String("path", srv.Member.BaseDir))
   101  
   102  	if err = srv.createEtcdLogFile(); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	srv.creatEtcdCmd(false)
   107  
   108  	if err = srv.saveTLSAssets(); err != nil {
   109  		return nil, err
   110  	}
   111  	if err = srv.startEtcdCmd(); err != nil {
   112  		return nil, err
   113  	}
   114  	srv.lg.Info("started etcd", zap.String("command-path", srv.etcdCmd.Path))
   115  	if err = srv.loadAutoTLSAssets(); err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	// wait some time for etcd listener start
   120  	// before setting up proxy
   121  	time.Sleep(time.Second)
   122  	if err = srv.startProxy(); err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	return &rpcpb.Response{
   127  		Success: true,
   128  		Status:  "start etcd PASS",
   129  		Member:  srv.Member,
   130  	}, nil
   131  }
   132  
   133  func (srv *Server) startProxy() error {
   134  	if srv.Member.EtcdClientProxy {
   135  		advertiseClientURL, advertiseClientURLPort, err := getURLAndPort(srv.Member.Etcd.AdvertiseClientURLs[0])
   136  		if err != nil {
   137  			return err
   138  		}
   139  		listenClientURL, _, err := getURLAndPort(srv.Member.Etcd.ListenClientURLs[0])
   140  		if err != nil {
   141  			return err
   142  		}
   143  
   144  		srv.advertiseClientPortToProxy[advertiseClientURLPort] = proxy.NewServer(proxy.ServerConfig{
   145  			Logger: srv.lg,
   146  			From:   *advertiseClientURL,
   147  			To:     *listenClientURL,
   148  		})
   149  		select {
   150  		case err = <-srv.advertiseClientPortToProxy[advertiseClientURLPort].Error():
   151  			return err
   152  		case <-time.After(2 * time.Second):
   153  			srv.lg.Info("started proxy on client traffic", zap.String("url", advertiseClientURL.String()))
   154  		}
   155  	}
   156  
   157  	if srv.Member.EtcdPeerProxy {
   158  		advertisePeerURL, advertisePeerURLPort, err := getURLAndPort(srv.Member.Etcd.AdvertisePeerURLs[0])
   159  		if err != nil {
   160  			return err
   161  		}
   162  		listenPeerURL, _, err := getURLAndPort(srv.Member.Etcd.ListenPeerURLs[0])
   163  		if err != nil {
   164  			return err
   165  		}
   166  
   167  		srv.advertisePeerPortToProxy[advertisePeerURLPort] = proxy.NewServer(proxy.ServerConfig{
   168  			Logger: srv.lg,
   169  			From:   *advertisePeerURL,
   170  			To:     *listenPeerURL,
   171  		})
   172  		select {
   173  		case err = <-srv.advertisePeerPortToProxy[advertisePeerURLPort].Error():
   174  			return err
   175  		case <-time.After(2 * time.Second):
   176  			srv.lg.Info("started proxy on peer traffic", zap.String("url", advertisePeerURL.String()))
   177  		}
   178  	}
   179  	return nil
   180  }
   181  
   182  func (srv *Server) stopProxy() {
   183  	if srv.Member.EtcdClientProxy && len(srv.advertiseClientPortToProxy) > 0 {
   184  		for port, px := range srv.advertiseClientPortToProxy {
   185  			if err := px.Close(); err != nil {
   186  				srv.lg.Warn("failed to close proxy", zap.Int("port", port))
   187  				continue
   188  			}
   189  			select {
   190  			case <-px.Done():
   191  				// enough time to release port
   192  				time.Sleep(time.Second)
   193  			case <-time.After(time.Second):
   194  			}
   195  			srv.lg.Info("closed proxy",
   196  				zap.Int("port", port),
   197  				zap.String("from", px.From()),
   198  				zap.String("to", px.To()),
   199  			)
   200  		}
   201  		srv.advertiseClientPortToProxy = make(map[int]proxy.Server)
   202  	}
   203  	if srv.Member.EtcdPeerProxy && len(srv.advertisePeerPortToProxy) > 0 {
   204  		for port, px := range srv.advertisePeerPortToProxy {
   205  			if err := px.Close(); err != nil {
   206  				srv.lg.Warn("failed to close proxy", zap.Int("port", port))
   207  				continue
   208  			}
   209  			select {
   210  			case <-px.Done():
   211  				// enough time to release port
   212  				time.Sleep(time.Second)
   213  			case <-time.After(time.Second):
   214  			}
   215  			srv.lg.Info("closed proxy",
   216  				zap.Int("port", port),
   217  				zap.String("from", px.From()),
   218  				zap.String("to", px.To()),
   219  			)
   220  		}
   221  		srv.advertisePeerPortToProxy = make(map[int]proxy.Server)
   222  	}
   223  }
   224  
   225  func (srv *Server) createEtcdLogFile() error {
   226  	var err error
   227  	srv.etcdLogFile, err = os.Create(srv.Member.EtcdLogPath)
   228  	if err != nil {
   229  		return err
   230  	}
   231  	srv.lg.Info("created etcd log file", zap.String("path", srv.Member.EtcdLogPath))
   232  	return nil
   233  }
   234  
   235  func (srv *Server) creatEtcdCmd(fromSnapshot bool) {
   236  	etcdPath, etcdFlags := srv.Member.EtcdExecPath, srv.Member.Etcd.Flags()
   237  	if fromSnapshot {
   238  		etcdFlags = srv.Member.EtcdOnSnapshotRestore.Flags()
   239  	}
   240  	u, _ := url.Parse(srv.Member.FailpointHTTPAddr)
   241  	srv.lg.Info("creating etcd command",
   242  		zap.String("etcd-exec-path", etcdPath),
   243  		zap.Strings("etcd-flags", etcdFlags),
   244  		zap.String("failpoint-http-addr", srv.Member.FailpointHTTPAddr),
   245  		zap.String("failpoint-addr", u.Host),
   246  	)
   247  	srv.etcdCmd = exec.Command(etcdPath, etcdFlags...)
   248  	srv.etcdCmd.Env = []string{"GOFAIL_HTTP=" + u.Host}
   249  	srv.etcdCmd.Stdout = srv.etcdLogFile
   250  	srv.etcdCmd.Stderr = srv.etcdLogFile
   251  }
   252  
   253  // if started with manual TLS, stores TLS assets
   254  // from tester/client to disk before starting etcd process
   255  func (srv *Server) saveTLSAssets() error {
   256  	if srv.Member.PeerCertPath != "" {
   257  		if srv.Member.PeerCertData == "" {
   258  			return fmt.Errorf("got empty data for %q", srv.Member.PeerCertPath)
   259  		}
   260  		if err := ioutil.WriteFile(srv.Member.PeerCertPath, []byte(srv.Member.PeerCertData), 0644); err != nil {
   261  			return err
   262  		}
   263  	}
   264  	if srv.Member.PeerKeyPath != "" {
   265  		if srv.Member.PeerKeyData == "" {
   266  			return fmt.Errorf("got empty data for %q", srv.Member.PeerKeyPath)
   267  		}
   268  		if err := ioutil.WriteFile(srv.Member.PeerKeyPath, []byte(srv.Member.PeerKeyData), 0644); err != nil {
   269  			return err
   270  		}
   271  	}
   272  	if srv.Member.PeerTrustedCAPath != "" {
   273  		if srv.Member.PeerTrustedCAData == "" {
   274  			return fmt.Errorf("got empty data for %q", srv.Member.PeerTrustedCAPath)
   275  		}
   276  		if err := ioutil.WriteFile(srv.Member.PeerTrustedCAPath, []byte(srv.Member.PeerTrustedCAData), 0644); err != nil {
   277  			return err
   278  		}
   279  	}
   280  	if srv.Member.PeerCertPath != "" &&
   281  		srv.Member.PeerKeyPath != "" &&
   282  		srv.Member.PeerTrustedCAPath != "" {
   283  		srv.lg.Info(
   284  			"wrote",
   285  			zap.String("peer-cert", srv.Member.PeerCertPath),
   286  			zap.String("peer-key", srv.Member.PeerKeyPath),
   287  			zap.String("peer-trusted-ca", srv.Member.PeerTrustedCAPath),
   288  		)
   289  	}
   290  
   291  	if srv.Member.ClientCertPath != "" {
   292  		if srv.Member.ClientCertData == "" {
   293  			return fmt.Errorf("got empty data for %q", srv.Member.ClientCertPath)
   294  		}
   295  		if err := ioutil.WriteFile(srv.Member.ClientCertPath, []byte(srv.Member.ClientCertData), 0644); err != nil {
   296  			return err
   297  		}
   298  	}
   299  	if srv.Member.ClientKeyPath != "" {
   300  		if srv.Member.ClientKeyData == "" {
   301  			return fmt.Errorf("got empty data for %q", srv.Member.ClientKeyPath)
   302  		}
   303  		if err := ioutil.WriteFile(srv.Member.ClientKeyPath, []byte(srv.Member.ClientKeyData), 0644); err != nil {
   304  			return err
   305  		}
   306  	}
   307  	if srv.Member.ClientTrustedCAPath != "" {
   308  		if srv.Member.ClientTrustedCAData == "" {
   309  			return fmt.Errorf("got empty data for %q", srv.Member.ClientTrustedCAPath)
   310  		}
   311  		if err := ioutil.WriteFile(srv.Member.ClientTrustedCAPath, []byte(srv.Member.ClientTrustedCAData), 0644); err != nil {
   312  			return err
   313  		}
   314  	}
   315  	if srv.Member.ClientCertPath != "" &&
   316  		srv.Member.ClientKeyPath != "" &&
   317  		srv.Member.ClientTrustedCAPath != "" {
   318  		srv.lg.Info(
   319  			"wrote",
   320  			zap.String("client-cert", srv.Member.ClientCertPath),
   321  			zap.String("client-key", srv.Member.ClientKeyPath),
   322  			zap.String("client-trusted-ca", srv.Member.ClientTrustedCAPath),
   323  		)
   324  	}
   325  
   326  	return nil
   327  }
   328  
   329  func (srv *Server) loadAutoTLSAssets() error {
   330  	if srv.Member.Etcd.PeerAutoTLS {
   331  		// in case of slow disk
   332  		time.Sleep(time.Second)
   333  
   334  		fdir := filepath.Join(srv.Member.Etcd.DataDir, "fixtures", "peer")
   335  
   336  		srv.lg.Info(
   337  			"loading client auto TLS assets",
   338  			zap.String("dir", fdir),
   339  			zap.String("endpoint", srv.EtcdClientEndpoint),
   340  		)
   341  
   342  		certPath := filepath.Join(fdir, "cert.pem")
   343  		if !fileutil.Exist(certPath) {
   344  			return fmt.Errorf("cannot find %q", certPath)
   345  		}
   346  		certData, err := ioutil.ReadFile(certPath)
   347  		if err != nil {
   348  			return fmt.Errorf("cannot read %q (%v)", certPath, err)
   349  		}
   350  		srv.Member.PeerCertData = string(certData)
   351  
   352  		keyPath := filepath.Join(fdir, "key.pem")
   353  		if !fileutil.Exist(keyPath) {
   354  			return fmt.Errorf("cannot find %q", keyPath)
   355  		}
   356  		keyData, err := ioutil.ReadFile(keyPath)
   357  		if err != nil {
   358  			return fmt.Errorf("cannot read %q (%v)", keyPath, err)
   359  		}
   360  		srv.Member.PeerKeyData = string(keyData)
   361  
   362  		srv.lg.Info(
   363  			"loaded peer auto TLS assets",
   364  			zap.String("peer-cert-path", certPath),
   365  			zap.Int("peer-cert-length", len(certData)),
   366  			zap.String("peer-key-path", keyPath),
   367  			zap.Int("peer-key-length", len(keyData)),
   368  		)
   369  	}
   370  
   371  	if srv.Member.Etcd.ClientAutoTLS {
   372  		// in case of slow disk
   373  		time.Sleep(time.Second)
   374  
   375  		fdir := filepath.Join(srv.Member.Etcd.DataDir, "fixtures", "client")
   376  
   377  		srv.lg.Info(
   378  			"loading client TLS assets",
   379  			zap.String("dir", fdir),
   380  			zap.String("endpoint", srv.EtcdClientEndpoint),
   381  		)
   382  
   383  		certPath := filepath.Join(fdir, "cert.pem")
   384  		if !fileutil.Exist(certPath) {
   385  			return fmt.Errorf("cannot find %q", certPath)
   386  		}
   387  		certData, err := ioutil.ReadFile(certPath)
   388  		if err != nil {
   389  			return fmt.Errorf("cannot read %q (%v)", certPath, err)
   390  		}
   391  		srv.Member.ClientCertData = string(certData)
   392  
   393  		keyPath := filepath.Join(fdir, "key.pem")
   394  		if !fileutil.Exist(keyPath) {
   395  			return fmt.Errorf("cannot find %q", keyPath)
   396  		}
   397  		keyData, err := ioutil.ReadFile(keyPath)
   398  		if err != nil {
   399  			return fmt.Errorf("cannot read %q (%v)", keyPath, err)
   400  		}
   401  		srv.Member.ClientKeyData = string(keyData)
   402  
   403  		srv.lg.Info(
   404  			"loaded client TLS assets",
   405  			zap.String("peer-cert-path", certPath),
   406  			zap.Int("peer-cert-length", len(certData)),
   407  			zap.String("peer-key-path", keyPath),
   408  			zap.Int("peer-key-length", len(keyData)),
   409  		)
   410  	}
   411  
   412  	return nil
   413  }
   414  
   415  // start but do not wait for it to complete
   416  func (srv *Server) startEtcdCmd() error {
   417  	return srv.etcdCmd.Start()
   418  }
   419  
   420  func (srv *Server) handle_RESTART_ETCD() (*rpcpb.Response, error) {
   421  	var err error
   422  	if !fileutil.Exist(srv.Member.BaseDir) {
   423  		err = fileutil.TouchDirAll(srv.Member.BaseDir)
   424  		if err != nil {
   425  			return nil, err
   426  		}
   427  	}
   428  
   429  	srv.creatEtcdCmd(false)
   430  
   431  	if err = srv.saveTLSAssets(); err != nil {
   432  		return nil, err
   433  	}
   434  	if err = srv.startEtcdCmd(); err != nil {
   435  		return nil, err
   436  	}
   437  	srv.lg.Info("restarted etcd", zap.String("command-path", srv.etcdCmd.Path))
   438  	if err = srv.loadAutoTLSAssets(); err != nil {
   439  		return nil, err
   440  	}
   441  
   442  	// wait some time for etcd listener start
   443  	// before setting up proxy
   444  	// TODO: local tests should handle port conflicts
   445  	// with clients on restart
   446  	time.Sleep(time.Second)
   447  	if err = srv.startProxy(); err != nil {
   448  		return nil, err
   449  	}
   450  
   451  	return &rpcpb.Response{
   452  		Success: true,
   453  		Status:  "restart etcd PASS",
   454  		Member:  srv.Member,
   455  	}, nil
   456  }
   457  
   458  func (srv *Server) handle_SIGTERM_ETCD() (*rpcpb.Response, error) {
   459  	srv.stopProxy()
   460  
   461  	err := stopWithSig(srv.etcdCmd, syscall.SIGTERM)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  	srv.lg.Info("killed etcd", zap.String("signal", syscall.SIGTERM.String()))
   466  
   467  	return &rpcpb.Response{
   468  		Success: true,
   469  		Status:  "killed etcd",
   470  	}, nil
   471  }
   472  
   473  func (srv *Server) handle_SIGQUIT_ETCD_AND_REMOVE_DATA() (*rpcpb.Response, error) {
   474  	srv.stopProxy()
   475  
   476  	err := stopWithSig(srv.etcdCmd, syscall.SIGQUIT)
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  	srv.lg.Info("killed etcd", zap.String("signal", syscall.SIGQUIT.String()))
   481  
   482  	srv.etcdLogFile.Sync()
   483  	srv.etcdLogFile.Close()
   484  
   485  	// for debugging purposes, rename instead of removing
   486  	if err = os.RemoveAll(srv.Member.BaseDir + ".backup"); err != nil {
   487  		return nil, err
   488  	}
   489  	if err = os.Rename(srv.Member.BaseDir, srv.Member.BaseDir+".backup"); err != nil {
   490  		return nil, err
   491  	}
   492  	srv.lg.Info(
   493  		"renamed",
   494  		zap.String("base-dir", srv.Member.BaseDir),
   495  		zap.String("new-dir", srv.Member.BaseDir+".backup"),
   496  	)
   497  
   498  	// create a new log file for next new member restart
   499  	if !fileutil.Exist(srv.Member.BaseDir) {
   500  		err = fileutil.TouchDirAll(srv.Member.BaseDir)
   501  		if err != nil {
   502  			return nil, err
   503  		}
   504  	}
   505  	if err = srv.createEtcdLogFile(); err != nil {
   506  		return nil, err
   507  	}
   508  
   509  	return &rpcpb.Response{
   510  		Success: true,
   511  		Status:  "killed etcd and removed base directory",
   512  	}, nil
   513  }
   514  
   515  func (srv *Server) handle_SAVE_SNAPSHOT() (*rpcpb.Response, error) {
   516  	err := srv.Member.SaveSnapshot(srv.lg)
   517  	if err != nil {
   518  		return nil, err
   519  	}
   520  	return &rpcpb.Response{
   521  		Success:      true,
   522  		Status:       "saved snapshot",
   523  		SnapshotInfo: srv.Member.SnapshotInfo,
   524  	}, nil
   525  }
   526  
   527  func (srv *Server) handle_RESTORE_RESTART_FROM_SNAPSHOT() (resp *rpcpb.Response, err error) {
   528  	err = srv.Member.RestoreSnapshot(srv.lg)
   529  	if err != nil {
   530  		return nil, err
   531  	}
   532  	resp, err = srv.handle_RESTART_FROM_SNAPSHOT()
   533  	if resp != nil && err == nil {
   534  		resp.Status = "restored snapshot and " + resp.Status
   535  	}
   536  	return resp, err
   537  }
   538  
   539  func (srv *Server) handle_RESTART_FROM_SNAPSHOT() (resp *rpcpb.Response, err error) {
   540  	srv.creatEtcdCmd(true)
   541  
   542  	if err = srv.saveTLSAssets(); err != nil {
   543  		return nil, err
   544  	}
   545  	if err = srv.startEtcdCmd(); err != nil {
   546  		return nil, err
   547  	}
   548  	srv.lg.Info("restarted etcd", zap.String("command-path", srv.etcdCmd.Path))
   549  	if err = srv.loadAutoTLSAssets(); err != nil {
   550  		return nil, err
   551  	}
   552  
   553  	// wait some time for etcd listener start
   554  	// before setting up proxy
   555  	// TODO: local tests should handle port conflicts
   556  	// with clients on restart
   557  	time.Sleep(time.Second)
   558  	if err = srv.startProxy(); err != nil {
   559  		return nil, err
   560  	}
   561  
   562  	return &rpcpb.Response{
   563  		Success:      true,
   564  		Status:       "restarted etcd from snapshot",
   565  		SnapshotInfo: srv.Member.SnapshotInfo,
   566  	}, nil
   567  }
   568  
   569  func (srv *Server) handle_SIGQUIT_ETCD_AND_ARCHIVE_DATA() (*rpcpb.Response, error) {
   570  	srv.stopProxy()
   571  
   572  	// exit with stackstrace
   573  	err := stopWithSig(srv.etcdCmd, syscall.SIGQUIT)
   574  	if err != nil {
   575  		return nil, err
   576  	}
   577  	srv.lg.Info("killed etcd", zap.String("signal", syscall.SIGQUIT.String()))
   578  
   579  	srv.etcdLogFile.Sync()
   580  	srv.etcdLogFile.Close()
   581  
   582  	// TODO: support separate WAL directory
   583  	if err = archive(
   584  		srv.Member.BaseDir,
   585  		srv.Member.EtcdLogPath,
   586  		srv.Member.Etcd.DataDir,
   587  	); err != nil {
   588  		return nil, err
   589  	}
   590  	srv.lg.Info("archived data", zap.String("base-dir", srv.Member.BaseDir))
   591  
   592  	if err = srv.createEtcdLogFile(); err != nil {
   593  		return nil, err
   594  	}
   595  
   596  	srv.lg.Info("cleaning up page cache")
   597  	if err := cleanPageCache(); err != nil {
   598  		srv.lg.Warn("failed to clean up page cache", zap.String("error", err.Error()))
   599  	}
   600  	srv.lg.Info("cleaned up page cache")
   601  
   602  	return &rpcpb.Response{
   603  		Success: true,
   604  		Status:  "cleaned up etcd",
   605  	}, nil
   606  }
   607  
   608  // stop proxy, etcd, delete data directory
   609  func (srv *Server) handle_SIGQUIT_ETCD_AND_REMOVE_DATA_AND_STOP_AGENT() (*rpcpb.Response, error) {
   610  	srv.stopProxy()
   611  
   612  	err := stopWithSig(srv.etcdCmd, syscall.SIGQUIT)
   613  	if err != nil {
   614  		return nil, err
   615  	}
   616  	srv.lg.Info("killed etcd", zap.String("signal", syscall.SIGQUIT.String()))
   617  
   618  	srv.etcdLogFile.Sync()
   619  	srv.etcdLogFile.Close()
   620  
   621  	err = os.RemoveAll(srv.Member.BaseDir)
   622  	if err != nil {
   623  		return nil, err
   624  	}
   625  	srv.lg.Info("removed base directory", zap.String("dir", srv.Member.BaseDir))
   626  
   627  	// stop agent server
   628  	srv.Stop()
   629  
   630  	return &rpcpb.Response{
   631  		Success: true,
   632  		Status:  "destroyed etcd and agent",
   633  	}, nil
   634  }
   635  
   636  func (srv *Server) handle_BLACKHOLE_PEER_PORT_TX_RX() (*rpcpb.Response, error) {
   637  	for port, px := range srv.advertisePeerPortToProxy {
   638  		srv.lg.Info("blackholing", zap.Int("peer-port", port))
   639  		px.BlackholeTx()
   640  		px.BlackholeRx()
   641  		srv.lg.Info("blackholed", zap.Int("peer-port", port))
   642  	}
   643  	return &rpcpb.Response{
   644  		Success: true,
   645  		Status:  "blackholed peer port tx/rx",
   646  	}, nil
   647  }
   648  
   649  func (srv *Server) handle_UNBLACKHOLE_PEER_PORT_TX_RX() (*rpcpb.Response, error) {
   650  	for port, px := range srv.advertisePeerPortToProxy {
   651  		srv.lg.Info("unblackholing", zap.Int("peer-port", port))
   652  		px.UnblackholeTx()
   653  		px.UnblackholeRx()
   654  		srv.lg.Info("unblackholed", zap.Int("peer-port", port))
   655  	}
   656  	return &rpcpb.Response{
   657  		Success: true,
   658  		Status:  "unblackholed peer port tx/rx",
   659  	}, nil
   660  }
   661  
   662  func (srv *Server) handle_DELAY_PEER_PORT_TX_RX() (*rpcpb.Response, error) {
   663  	lat := time.Duration(srv.Tester.UpdatedDelayLatencyMs) * time.Millisecond
   664  	rv := time.Duration(srv.Tester.DelayLatencyMsRv) * time.Millisecond
   665  
   666  	for port, px := range srv.advertisePeerPortToProxy {
   667  		srv.lg.Info("delaying",
   668  			zap.Int("peer-port", port),
   669  			zap.Duration("latency", lat),
   670  			zap.Duration("random-variable", rv),
   671  		)
   672  		px.DelayTx(lat, rv)
   673  		px.DelayRx(lat, rv)
   674  		srv.lg.Info("delayed",
   675  			zap.Int("peer-port", port),
   676  			zap.Duration("latency", lat),
   677  			zap.Duration("random-variable", rv),
   678  		)
   679  	}
   680  
   681  	return &rpcpb.Response{
   682  		Success: true,
   683  		Status:  "delayed peer port tx/rx",
   684  	}, nil
   685  }
   686  
   687  func (srv *Server) handle_UNDELAY_PEER_PORT_TX_RX() (*rpcpb.Response, error) {
   688  	for port, px := range srv.advertisePeerPortToProxy {
   689  		srv.lg.Info("undelaying", zap.Int("peer-port", port))
   690  		px.UndelayTx()
   691  		px.UndelayRx()
   692  		srv.lg.Info("undelayed", zap.Int("peer-port", port))
   693  	}
   694  	return &rpcpb.Response{
   695  		Success: true,
   696  		Status:  "undelayed peer port tx/rx",
   697  	}, nil
   698  }