github.com/kjdelisle/consul@v1.4.5/command/exec/exec_test.go (about)

     1  package exec
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/consul/testrpc"
     9  
    10  	"github.com/hashicorp/consul/agent"
    11  	consulapi "github.com/hashicorp/consul/api"
    12  	"github.com/hashicorp/consul/testutil/retry"
    13  	"github.com/mitchellh/cli"
    14  )
    15  
    16  func TestExecCommand_noTabs(t *testing.T) {
    17  	t.Parallel()
    18  	if strings.ContainsRune(New(nil, nil).Help(), '\t') {
    19  		t.Fatal("help has tabs")
    20  	}
    21  }
    22  
    23  func TestExecCommand(t *testing.T) {
    24  	t.Parallel()
    25  	a := agent.NewTestAgent(t, t.Name(), `
    26  		disable_remote_exec = false
    27  	`)
    28  	defer a.Shutdown()
    29  
    30  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
    31  
    32  	ui := cli.NewMockUi()
    33  	c := New(ui, nil)
    34  	args := []string{"-http-addr=" + a.HTTPAddr(), "-wait=1s", "uptime"}
    35  
    36  	code := c.Run(args)
    37  	if code != 0 {
    38  		t.Fatalf("bad: %d. Error:%#v  (std)Output:%#v", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
    39  	}
    40  
    41  	if !strings.Contains(ui.OutputWriter.String(), "load") {
    42  		t.Fatalf("bad: %#v", ui.OutputWriter.String())
    43  	}
    44  }
    45  
    46  func TestExecCommand_NoShell(t *testing.T) {
    47  	t.Parallel()
    48  	a := agent.NewTestAgent(t, t.Name(), `
    49  		disable_remote_exec = false
    50  	`)
    51  	defer a.Shutdown()
    52  
    53  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
    54  
    55  	ui := cli.NewMockUi()
    56  	c := New(ui, nil)
    57  	args := []string{"-http-addr=" + a.HTTPAddr(), "-shell=false", "-wait=1s", "uptime"}
    58  
    59  	code := c.Run(args)
    60  	if code != 0 {
    61  		t.Fatalf("bad: %d. Error:%#v  (std)Output:%#v", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
    62  	}
    63  
    64  	if !strings.Contains(ui.OutputWriter.String(), "load") {
    65  		t.Fatalf("bad: %#v", ui.OutputWriter.String())
    66  	}
    67  }
    68  
    69  func TestExecCommand_CrossDC(t *testing.T) {
    70  	t.Parallel()
    71  	a1 := agent.NewTestAgent(t, t.Name(), `
    72  		disable_remote_exec = false
    73  	`)
    74  	defer a1.Shutdown()
    75  
    76  	testrpc.WaitForTestAgent(t, a1.RPC, "dc1")
    77  
    78  	a2 := agent.NewTestAgent(t, t.Name(), `
    79  		datacenter = "dc2"
    80  		disable_remote_exec = false
    81  	`)
    82  	defer a2.Shutdown()
    83  
    84  	testrpc.WaitForTestAgent(t, a2.RPC, "dc2")
    85  
    86  	// Join over the WAN
    87  	_, err := a2.JoinWAN([]string{a1.Config.SerfBindAddrWAN.String()})
    88  	if err != nil {
    89  		t.Fatalf("err: %v", err)
    90  	}
    91  
    92  	if got, want := len(a1.WANMembers()), 2; got != want {
    93  		t.Fatalf("got %d WAN members on a1 want %d", got, want)
    94  	}
    95  	if got, want := len(a2.WANMembers()), 2; got != want {
    96  		t.Fatalf("got %d WAN members on a2 want %d", got, want)
    97  	}
    98  
    99  	ui := cli.NewMockUi()
   100  	c := New(ui, nil)
   101  	args := []string{"-http-addr=" + a1.HTTPAddr(), "-wait=500ms", "-datacenter=dc2", "uptime"}
   102  
   103  	code := c.Run(args)
   104  	if code != 0 {
   105  		t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String())
   106  	}
   107  
   108  	if !strings.Contains(ui.OutputWriter.String(), "load") {
   109  		t.Fatalf("bad: %#v", ui.OutputWriter.String())
   110  	}
   111  }
   112  
   113  func TestExecCommand_Validate(t *testing.T) {
   114  	t.Parallel()
   115  	conf := &rExecConf{}
   116  	err := conf.validate()
   117  	if err != nil {
   118  		t.Fatalf("err: %v", err)
   119  	}
   120  
   121  	conf.node = "("
   122  	err = conf.validate()
   123  	if err == nil {
   124  		t.Fatalf("err: %v", err)
   125  	}
   126  
   127  	conf.node = ""
   128  	conf.service = "("
   129  	err = conf.validate()
   130  	if err == nil {
   131  		t.Fatalf("err: %v", err)
   132  	}
   133  
   134  	conf.service = "()"
   135  	conf.tag = "("
   136  	err = conf.validate()
   137  	if err == nil {
   138  		t.Fatalf("err: %v", err)
   139  	}
   140  
   141  	conf.service = ""
   142  	conf.tag = "foo"
   143  	err = conf.validate()
   144  	if err == nil {
   145  		t.Fatalf("err: %v", err)
   146  	}
   147  }
   148  
   149  func TestExecCommand_Sessions(t *testing.T) {
   150  	t.Parallel()
   151  	a := agent.NewTestAgent(t, t.Name(), `
   152  		disable_remote_exec = false
   153  	`)
   154  	defer a.Shutdown()
   155  
   156  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
   157  
   158  	ui := cli.NewMockUi()
   159  	c := New(ui, nil)
   160  	c.apiclient = a.Client()
   161  	id, err := c.createSession()
   162  	if err != nil {
   163  		t.Fatalf("err: %v", err)
   164  	}
   165  
   166  	se, _, err := a.Client().Session().Info(id, nil)
   167  	if err != nil {
   168  		t.Fatalf("err: %v", err)
   169  	}
   170  	if se == nil || se.Name != "Remote Exec" {
   171  		t.Fatalf("bad: %v", se)
   172  	}
   173  
   174  	c.sessionID = id
   175  	err = c.destroySession()
   176  	if err != nil {
   177  		t.Fatalf("err: %v", err)
   178  	}
   179  
   180  	se, _, err = a.Client().Session().Info(id, nil)
   181  	if err != nil {
   182  		t.Fatalf("err: %v", err)
   183  	}
   184  	if se != nil {
   185  		t.Fatalf("bad: %v", se)
   186  	}
   187  }
   188  
   189  func TestExecCommand_Sessions_Foreign(t *testing.T) {
   190  	t.Parallel()
   191  	a := agent.NewTestAgent(t, t.Name(), `
   192  		disable_remote_exec = false
   193  	`)
   194  	defer a.Shutdown()
   195  
   196  	ui := cli.NewMockUi()
   197  	c := New(ui, nil)
   198  	c.apiclient = a.Client()
   199  
   200  	c.conf.foreignDC = true
   201  	c.conf.localDC = "dc1"
   202  	c.conf.localNode = "foo"
   203  
   204  	var id string
   205  	retry.Run(t, func(r *retry.R) {
   206  		var err error
   207  		id, err = c.createSession()
   208  		if err != nil {
   209  			r.Fatal(err)
   210  		}
   211  		if id == "" {
   212  			r.Fatal("no id")
   213  		}
   214  	})
   215  
   216  	se, _, err := a.Client().Session().Info(id, nil)
   217  	if err != nil {
   218  		t.Fatalf("err: %v", err)
   219  	}
   220  	if se == nil || se.Name != "Remote Exec via foo@dc1" {
   221  		t.Fatalf("bad: %v", se)
   222  	}
   223  
   224  	c.sessionID = id
   225  	err = c.destroySession()
   226  	if err != nil {
   227  		t.Fatalf("err: %v", err)
   228  	}
   229  
   230  	se, _, err = a.Client().Session().Info(id, nil)
   231  	if err != nil {
   232  		t.Fatalf("err: %v", err)
   233  	}
   234  	if se != nil {
   235  		t.Fatalf("bad: %v", se)
   236  	}
   237  }
   238  
   239  func TestExecCommand_UploadDestroy(t *testing.T) {
   240  	t.Parallel()
   241  	a := agent.NewTestAgent(t, t.Name(), `
   242  		disable_remote_exec = false
   243  	`)
   244  	defer a.Shutdown()
   245  
   246  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
   247  
   248  	ui := cli.NewMockUi()
   249  
   250  	c := New(ui, nil)
   251  	c.apiclient = a.Client()
   252  	id, err := c.createSession()
   253  	if err != nil {
   254  		t.Fatalf("err: %v", err)
   255  	}
   256  	c.sessionID = id
   257  
   258  	c.conf.prefix = "_rexec"
   259  	c.conf.cmd = "uptime"
   260  	c.conf.wait = time.Second
   261  
   262  	buf, err := c.makeRExecSpec()
   263  	if err != nil {
   264  		t.Fatalf("err: %v", err)
   265  	}
   266  
   267  	err = c.uploadPayload(buf)
   268  	if err != nil {
   269  		t.Fatalf("err: %v", err)
   270  	}
   271  
   272  	pair, _, err := a.Client().KV().Get("_rexec/"+id+"/job", nil)
   273  	if err != nil {
   274  		t.Fatalf("err: %v", err)
   275  	}
   276  
   277  	if pair == nil || len(pair.Value) == 0 {
   278  		t.Fatalf("missing job spec")
   279  	}
   280  
   281  	err = c.destroyData()
   282  	if err != nil {
   283  		t.Fatalf("err: %v", err)
   284  	}
   285  
   286  	pair, _, err = a.Client().KV().Get("_rexec/"+id+"/job", nil)
   287  	if err != nil {
   288  		t.Fatalf("err: %v", err)
   289  	}
   290  
   291  	if pair != nil {
   292  		t.Fatalf("should be destroyed")
   293  	}
   294  }
   295  
   296  func TestExecCommand_StreamResults(t *testing.T) {
   297  	t.Parallel()
   298  	a := agent.NewTestAgent(t, t.Name(), `
   299  		disable_remote_exec = false
   300  	`)
   301  	defer a.Shutdown()
   302  
   303  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
   304  
   305  	ui := cli.NewMockUi()
   306  	c := New(ui, nil)
   307  	c.apiclient = a.Client()
   308  	c.conf.prefix = "_rexec"
   309  
   310  	id, err := c.createSession()
   311  	if err != nil {
   312  		t.Fatalf("err: %v", err)
   313  	}
   314  	c.sessionID = id
   315  
   316  	ackCh := make(chan rExecAck, 128)
   317  	heartCh := make(chan rExecHeart, 128)
   318  	outputCh := make(chan rExecOutput, 128)
   319  	exitCh := make(chan rExecExit, 128)
   320  	doneCh := make(chan struct{})
   321  	errCh := make(chan struct{}, 1)
   322  	defer close(doneCh)
   323  	go c.streamResults(doneCh, ackCh, heartCh, outputCh, exitCh, errCh)
   324  
   325  	prefix := "_rexec/" + id + "/"
   326  	ok, _, err := a.Client().KV().Acquire(&consulapi.KVPair{
   327  		Key:     prefix + "foo/ack",
   328  		Session: id,
   329  	}, nil)
   330  	if err != nil {
   331  		t.Fatalf("err: %v", err)
   332  	}
   333  	if !ok {
   334  		t.Fatalf("should be ok bro")
   335  	}
   336  
   337  	retry.Run(t, func(r *retry.R) {
   338  		select {
   339  		case a := <-ackCh:
   340  			if a.Node != "foo" {
   341  				r.Fatalf("bad: %#v", a)
   342  			}
   343  		case <-time.After(50 * time.Millisecond):
   344  			r.Fatalf("timeout")
   345  		}
   346  	})
   347  
   348  	ok, _, err = a.Client().KV().Acquire(&consulapi.KVPair{
   349  		Key:     prefix + "foo/exit",
   350  		Value:   []byte("127"),
   351  		Session: id,
   352  	}, nil)
   353  	if err != nil {
   354  		t.Fatalf("err: %v", err)
   355  	}
   356  	if !ok {
   357  		t.Fatalf("should be ok bro")
   358  	}
   359  
   360  	retry.Run(t, func(r *retry.R) {
   361  		select {
   362  		case e := <-exitCh:
   363  			if e.Node != "foo" || e.Code != 127 {
   364  				r.Fatalf("bad: %#v", e)
   365  			}
   366  		case <-time.After(50 * time.Millisecond):
   367  			r.Fatalf("timeout")
   368  		}
   369  	})
   370  
   371  	// Random key, should ignore
   372  	ok, _, err = a.Client().KV().Acquire(&consulapi.KVPair{
   373  		Key:     prefix + "foo/random",
   374  		Session: id,
   375  	}, nil)
   376  	if err != nil {
   377  		t.Fatalf("err: %v", err)
   378  	}
   379  	if !ok {
   380  		t.Fatalf("should be ok bro")
   381  	}
   382  
   383  	// Output heartbeat
   384  	ok, _, err = a.Client().KV().Acquire(&consulapi.KVPair{
   385  		Key:     prefix + "foo/out/00000",
   386  		Session: id,
   387  	}, nil)
   388  	if err != nil {
   389  		t.Fatalf("err: %v", err)
   390  	}
   391  	if !ok {
   392  		t.Fatalf("should be ok bro")
   393  	}
   394  
   395  	retry.Run(t, func(r *retry.R) {
   396  		select {
   397  		case h := <-heartCh:
   398  			if h.Node != "foo" {
   399  				r.Fatalf("bad: %#v", h)
   400  			}
   401  		case <-time.After(50 * time.Millisecond):
   402  			r.Fatalf("timeout")
   403  		}
   404  	})
   405  
   406  	// Output value
   407  	ok, _, err = a.Client().KV().Acquire(&consulapi.KVPair{
   408  		Key:     prefix + "foo/out/00001",
   409  		Value:   []byte("test"),
   410  		Session: id,
   411  	}, nil)
   412  	if err != nil {
   413  		t.Fatalf("err: %v", err)
   414  	}
   415  	if !ok {
   416  		t.Fatalf("should be ok bro")
   417  	}
   418  
   419  	retry.Run(t, func(r *retry.R) {
   420  		select {
   421  		case o := <-outputCh:
   422  			if o.Node != "foo" || string(o.Output) != "test" {
   423  				r.Fatalf("bad: %#v", o)
   424  			}
   425  		case <-time.After(50 * time.Millisecond):
   426  			r.Fatalf("timeout")
   427  		}
   428  	})
   429  }