gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/hookstate/ctlcmd/ctlcmd_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package ctlcmd_test
    21  
    22  import (
    23  	"fmt"
    24  	"strings"
    25  	"testing"
    26  
    27  	"github.com/jessevdk/go-flags"
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/overlord/hookstate"
    31  	"github.com/snapcore/snapd/overlord/hookstate/ctlcmd"
    32  	"github.com/snapcore/snapd/overlord/hookstate/hooktest"
    33  	"github.com/snapcore/snapd/overlord/state"
    34  	"github.com/snapcore/snapd/snap"
    35  	"github.com/snapcore/snapd/testutil"
    36  )
    37  
    38  func Test(t *testing.T) { TestingT(t) }
    39  
    40  type ctlcmdSuite struct {
    41  	mockContext *hookstate.Context
    42  }
    43  
    44  var _ = Suite(&ctlcmdSuite{})
    45  
    46  func (s *ctlcmdSuite) SetUpTest(c *C) {
    47  	handler := hooktest.NewMockHandler()
    48  
    49  	state := state.New(nil)
    50  	state.Lock()
    51  	defer state.Unlock()
    52  
    53  	task := state.NewTask("test-task", "my test task")
    54  	setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"}
    55  
    56  	var err error
    57  	s.mockContext, err = hookstate.NewContext(task, task.State(), setup, handler, "")
    58  	c.Assert(err, IsNil)
    59  }
    60  
    61  func (s *ctlcmdSuite) TestNonExistingCommand(c *C) {
    62  	stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"foo"}, 0)
    63  	c.Check(string(stdout), Equals, "")
    64  	c.Check(string(stderr), Equals, "")
    65  	c.Check(err, ErrorMatches, ".*[Uu]nknown command.*")
    66  }
    67  
    68  func (s *ctlcmdSuite) TestCommandOutput(c *C) {
    69  	mockCommand := ctlcmd.AddMockCommand("mock")
    70  	defer ctlcmd.RemoveCommand("mock")
    71  
    72  	mockCommand.FakeStdout = "test stdout"
    73  	mockCommand.FakeStderr = "test stderr"
    74  
    75  	stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"mock", "foo"}, 0)
    76  	c.Check(err, IsNil)
    77  	c.Check(string(stdout), Equals, "test stdout")
    78  	c.Check(string(stderr), Equals, "test stderr")
    79  	c.Check(mockCommand.Args, DeepEquals, []string{"foo"})
    80  }
    81  
    82  func taskKinds(tasks []*state.Task) []string {
    83  	kinds := make([]string, len(tasks))
    84  	for i, task := range tasks {
    85  		k := task.Kind()
    86  		if k == "run-hook" {
    87  			var hooksup hookstate.HookSetup
    88  			if err := task.Get("hook-setup", &hooksup); err != nil {
    89  				panic(err)
    90  			}
    91  			k = fmt.Sprintf("%s[%s]", k, hooksup.Hook)
    92  		}
    93  		kinds[i] = k
    94  	}
    95  	return kinds
    96  }
    97  
    98  func (s *ctlcmdSuite) TestHiddenCommand(c *C) {
    99  	ctlcmd.AddHiddenMockCommand("mock-hidden")
   100  	ctlcmd.AddMockCommand("mock-shown")
   101  	defer ctlcmd.RemoveCommand("mock-hidden")
   102  	defer ctlcmd.RemoveCommand("mock-shown")
   103  
   104  	_, _, err := ctlcmd.Run(s.mockContext, []string{"--help"}, 0)
   105  	// help message output is returned as *flags.Error with
   106  	// Type as flags.ErrHelp
   107  	c.Assert(err, FitsTypeOf, &flags.Error{})
   108  	c.Check(err.(*flags.Error).Type, Equals, flags.ErrHelp)
   109  	// snapctl is mentioned (not snapd)
   110  	c.Check(err.Error(), testutil.Contains, "snapctl")
   111  	// mock-shown is in the help message
   112  	c.Check(err.Error(), testutil.Contains, "  mock-shown\n")
   113  	// mock-hidden is not in the help message
   114  	c.Check(err.Error(), Not(testutil.Contains), "  mock-hidden\n")
   115  }
   116  
   117  func (s *ctlcmdSuite) TestRootRequiredCommandFailure(c *C) {
   118  	_, _, err := ctlcmd.Run(s.mockContext, []string{"start"}, 1000)
   119  
   120  	c.Check(err, FitsTypeOf, &ctlcmd.ForbiddenCommandError{})
   121  	c.Check(err.Error(), Equals, `cannot use "start" with uid 1000, try with sudo`)
   122  }
   123  
   124  func (s *ctlcmdSuite) TestRunNoArgsFailure(c *C) {
   125  	_, _, err := ctlcmd.Run(s.mockContext, []string{}, 0)
   126  	c.Check(err, NotNil)
   127  }
   128  
   129  func (s *ctlcmdSuite) TestRunOnlyHelp(c *C) {
   130  	_, _, err := ctlcmd.Run(s.mockContext, []string{"-h"}, 1000)
   131  	c.Check(err, NotNil)
   132  	c.Assert(strings.HasPrefix(err.Error(), "Usage:"), Equals, true)
   133  
   134  	_, _, err = ctlcmd.Run(s.mockContext, []string{"--help"}, 1000)
   135  	c.Check(err, NotNil)
   136  	c.Assert(strings.HasPrefix(err.Error(), "Usage:"), Equals, true)
   137  }
   138  
   139  func (s *ctlcmdSuite) TestRunHelpAtAnyPosition(c *C) {
   140  	_, _, err := ctlcmd.Run(s.mockContext, []string{"start", "a", "-h"}, 1000)
   141  	c.Check(err, NotNil)
   142  	c.Assert(strings.HasPrefix(err.Error(), "Usage:"), Equals, true)
   143  
   144  	_, _, err = ctlcmd.Run(s.mockContext, []string{"start", "a", "b", "--help"}, 1000)
   145  	c.Check(err, NotNil)
   146  	c.Assert(strings.HasPrefix(err.Error(), "Usage:"), Equals, true)
   147  }
   148  
   149  func (s *ctlcmdSuite) TestRunNonRootAllowedCommandWithAllowedCmdAsArg(c *C) {
   150  	// this test protects us against a future refactor introducing a bug that allows
   151  	// a root-only command to run without root if an arg is in the nonRootAllowed list
   152  	_, _, err := ctlcmd.Run(s.mockContext, []string{"set", "get", "a"}, 1000)
   153  	c.Check(err, FitsTypeOf, &ctlcmd.ForbiddenCommandError{})
   154  	c.Check(err.Error(), Equals, `cannot use "set" with uid 1000, try with sudo`)
   155  }