github.com/smithx10/nomad@v0.9.1-rc1/e2e/execagent/execagent.go (about)

     1  package execagent
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"net"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"text/template"
    12  
    13  	"github.com/hashicorp/nomad/api"
    14  )
    15  
    16  type AgentMode int
    17  
    18  const (
    19  	// Conf enum is for configuring either a client, server, or mixed agent.
    20  	ModeClient AgentMode = 1
    21  	ModeServer AgentMode = 2
    22  	ModeBoth             = ModeClient | ModeServer
    23  )
    24  
    25  func init() {
    26  	if d := os.Getenv("NOMAD_TEST_DIR"); d != "" {
    27  		BaseDir = d
    28  	}
    29  }
    30  
    31  var (
    32  	// BaseDir is where tests will store state and can be overridden by
    33  	// setting NOMAD_TEST_DIR. Defaults to "/opt/nomadtest"
    34  	BaseDir = "/opt/nomadtest"
    35  
    36  	agentTemplate = template.Must(template.New("agent").Parse(`
    37  enable_debug = true
    38  log_level = "{{ or .LogLevel "DEBUG" }}"
    39  
    40  ports {
    41    http = {{.HTTP}}
    42    rpc  = {{.RPC}}
    43    serf = {{.Serf}}
    44  }
    45  
    46  {{ if .EnableServer }}
    47  server {
    48    enabled = true
    49    bootstrap_expect = 1
    50  }
    51  {{ end }}
    52  
    53  {{ if .EnableClient }}
    54  client {
    55    enabled = true
    56    options = {
    57      "driver.raw_exec.enable" = "1"
    58    }
    59  }
    60  {{ end }}
    61  `))
    62  )
    63  
    64  type AgentTemplateVars struct {
    65  	HTTP         int
    66  	RPC          int
    67  	Serf         int
    68  	EnableClient bool
    69  	EnableServer bool
    70  	LogLevel     string
    71  }
    72  
    73  func newAgentTemplateVars() (*AgentTemplateVars, error) {
    74  	httpPort, err := getFreePort()
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	rpcPort, err := getFreePort()
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	serfPort, err := getFreePort()
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	vars := AgentTemplateVars{
    88  		HTTP: httpPort,
    89  		RPC:  rpcPort,
    90  		Serf: serfPort,
    91  	}
    92  
    93  	return &vars, nil
    94  }
    95  
    96  func writeConfig(path string, vars *AgentTemplateVars) error {
    97  	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	defer f.Close()
   102  	return agentTemplate.Execute(f, vars)
   103  }
   104  
   105  // NomadAgent manages an external Nomad agent process.
   106  type NomadAgent struct {
   107  	// BinPath is the path to the Nomad binary
   108  	BinPath string
   109  
   110  	// DataDir is the path state will be saved in
   111  	DataDir string
   112  
   113  	// ConfFile is the path to the agent's conf file
   114  	ConfFile string
   115  
   116  	// Cmd is the agent process
   117  	Cmd *exec.Cmd
   118  
   119  	// Vars are the config parameters used to template
   120  	Vars *AgentTemplateVars
   121  }
   122  
   123  // NewMixedAgent creates a new Nomad agent in mixed server+client mode but does
   124  // not start the agent process until the Start() method is called.
   125  func NewMixedAgent(bin string) (*NomadAgent, error) {
   126  	if err := os.MkdirAll(BaseDir, 755); err != nil {
   127  		return nil, err
   128  	}
   129  	dir, err := ioutil.TempDir(BaseDir, "agent")
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	vars, err := newAgentTemplateVars()
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	vars.EnableClient = true
   139  	vars.EnableServer = true
   140  
   141  	conf := filepath.Join(dir, "config.hcl")
   142  	if err := writeConfig(conf, vars); err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	na := &NomadAgent{
   147  		BinPath:  bin,
   148  		DataDir:  dir,
   149  		ConfFile: conf,
   150  		Vars:     vars,
   151  		Cmd:      exec.Command(bin, "agent", "-config", conf, "-data-dir", dir),
   152  	}
   153  	return na, nil
   154  }
   155  
   156  // NewClientServerPair creates a pair of Nomad agents: 1 server, 1 client.
   157  func NewClientServerPair(bin string, serverOut, clientOut io.Writer) (
   158  	server *NomadAgent, client *NomadAgent, err error) {
   159  
   160  	if err := os.MkdirAll(BaseDir, 755); err != nil {
   161  		return nil, nil, err
   162  	}
   163  
   164  	sdir, err := ioutil.TempDir(BaseDir, "server")
   165  	if err != nil {
   166  		return nil, nil, err
   167  	}
   168  
   169  	svars, err := newAgentTemplateVars()
   170  	if err != nil {
   171  		return nil, nil, err
   172  	}
   173  	svars.LogLevel = "WARN"
   174  	svars.EnableServer = true
   175  
   176  	sconf := filepath.Join(sdir, "config.hcl")
   177  	if err := writeConfig(sconf, svars); err != nil {
   178  		return nil, nil, err
   179  	}
   180  
   181  	server = &NomadAgent{
   182  		BinPath:  bin,
   183  		DataDir:  sdir,
   184  		ConfFile: sconf,
   185  		Vars:     svars,
   186  		Cmd:      exec.Command(bin, "agent", "-config", sconf, "-data-dir", sdir),
   187  	}
   188  	server.Cmd.Stdout = serverOut
   189  	server.Cmd.Stderr = serverOut
   190  
   191  	cdir, err := ioutil.TempDir(BaseDir, "client")
   192  	if err != nil {
   193  		return nil, nil, err
   194  	}
   195  
   196  	cvars, err := newAgentTemplateVars()
   197  	if err != nil {
   198  		return nil, nil, err
   199  	}
   200  	cvars.EnableClient = true
   201  
   202  	cconf := filepath.Join(cdir, "config.hcl")
   203  	if err := writeConfig(cconf, cvars); err != nil {
   204  		return nil, nil, err
   205  	}
   206  
   207  	client = &NomadAgent{
   208  		BinPath:  bin,
   209  		DataDir:  cdir,
   210  		ConfFile: cconf,
   211  		Vars:     cvars,
   212  		Cmd: exec.Command(bin, "agent",
   213  			"-config", cconf,
   214  			"-data-dir", cdir,
   215  			"-servers", fmt.Sprintf("127.0.0.1:%d", svars.RPC),
   216  		),
   217  	}
   218  	client.Cmd.Stdout = clientOut
   219  	client.Cmd.Stderr = clientOut
   220  	return
   221  }
   222  
   223  // Start the agent command.
   224  func (n *NomadAgent) Start() error {
   225  	return n.Cmd.Start()
   226  }
   227  
   228  // Stop sends an interrupt signal and returns the command's Wait error.
   229  func (n *NomadAgent) Stop() error {
   230  	if err := n.Cmd.Process.Signal(os.Interrupt); err != nil {
   231  		return err
   232  	}
   233  
   234  	return n.Cmd.Wait()
   235  }
   236  
   237  // Destroy stops the agent and removes the data dir.
   238  func (n *NomadAgent) Destroy() error {
   239  	if err := n.Stop(); err != nil {
   240  		return err
   241  	}
   242  	return os.RemoveAll(n.DataDir)
   243  }
   244  
   245  // Client returns an api.Client for the agent.
   246  func (n *NomadAgent) Client() (*api.Client, error) {
   247  	conf := api.DefaultConfig()
   248  	conf.Address = fmt.Sprintf("http://127.0.0.1:%d", n.Vars.HTTP)
   249  	return api.NewClient(conf)
   250  }
   251  
   252  func getFreePort() (int, error) {
   253  	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
   254  	if err != nil {
   255  		return 0, err
   256  	}
   257  
   258  	l, err := net.ListenTCP("tcp", addr)
   259  	if err != nil {
   260  		return 0, err
   261  	}
   262  	defer l.Close()
   263  	return l.Addr().(*net.TCPAddr).Port, nil
   264  }