github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/uniter/runner/jujuc/relation-get_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Copyright 2014 Cloudbase Solutions SRL
     3  // Licensed under the AGPLv3, see LICENCE file for details.
     4  
     5  package jujuc_test
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/juju/cmd/v3"
    13  	"github.com/juju/cmd/v3/cmdtesting"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/worker/uniter/runner/jujuc"
    18  	"github.com/juju/juju/worker/uniter/runner/jujuc/jujuctesting"
    19  )
    20  
    21  type RelationGetSuite struct {
    22  	relationSuite
    23  }
    24  
    25  var _ = gc.Suite(&RelationGetSuite{})
    26  
    27  func (s *RelationGetSuite) newHookContext(relid int, remote string, app string) (jujuc.Context, *relationInfo) {
    28  	hctx, info := s.relationSuite.newHookContext(relid, remote, app)
    29  	info.rels[0].Units["u/0"]["private-address"] = "foo: bar\n"
    30  	info.rels[1].SetRelated("m/0", jujuctesting.Settings{"pew": "pew\npew\n"})
    31  	info.rels[1].SetRelated("u/1", jujuctesting.Settings{"value": "12345"})
    32  	return hctx, info
    33  }
    34  
    35  var relationGetTests = []struct {
    36  	summary     string
    37  	relid       int
    38  	unit        string
    39  	args        []string
    40  	code        int
    41  	out         string
    42  	key         string
    43  	application bool
    44  }{
    45  	{
    46  		summary: "no default relation",
    47  		relid:   -1,
    48  		code:    2,
    49  		out:     `no relation id specified`,
    50  	}, {
    51  		summary: "explicit relation, not known",
    52  		relid:   -1,
    53  		code:    2,
    54  		args:    []string{"-r", "burble:123"},
    55  		out:     `invalid value "burble:123" for option -r: relation not found`,
    56  	}, {
    57  		summary: "default relation, no unit chosen",
    58  		relid:   1,
    59  		code:    2,
    60  		out:     `no unit or application specified`,
    61  	}, {
    62  		summary: "explicit relation, no unit chosen",
    63  		relid:   -1,
    64  		code:    2,
    65  		args:    []string{"-r", "burble:1"},
    66  		out:     `no unit or application specified`,
    67  	}, {
    68  		summary: "missing key",
    69  		relid:   1,
    70  		unit:    "m/0",
    71  		args:    []string{"ker-plunk"},
    72  	}, {
    73  		summary: "missing unit",
    74  		relid:   1,
    75  		unit:    "bad/0",
    76  		code:    1,
    77  		out:     `unknown unit bad/0`,
    78  	}, {
    79  		summary: "all keys with explicit non-member",
    80  		relid:   1,
    81  		args:    []string{"-", "u/1"},
    82  		out:     `value: "12345"`,
    83  	}, {
    84  		summary: "specific key with implicit member",
    85  		relid:   1,
    86  		unit:    "m/0",
    87  		args:    []string{"pew"},
    88  		out:     "pew\npew\n",
    89  	}, {
    90  		summary: "specific key with explicit member",
    91  		relid:   1,
    92  		args:    []string{"pew", "m/0"},
    93  		out:     "pew\npew\n",
    94  	}, {
    95  		summary: "specific key with explicit non-member",
    96  		relid:   1,
    97  		args:    []string{"value", "u/1"},
    98  		out:     "12345",
    99  	}, {
   100  		summary: "specific key with explicit local",
   101  		relid:   0,
   102  		args:    []string{"private-address", "u/0"},
   103  		out:     "foo: bar\n",
   104  	}, {
   105  		summary: "all keys with implicit member",
   106  		relid:   1,
   107  		unit:    "m/0",
   108  		out:     "pew: |\n  pew\n  pew",
   109  	}, {
   110  		summary: "all keys with explicit member",
   111  		relid:   1,
   112  		args:    []string{"-", "m/0"},
   113  		out:     "pew: |\n  pew\n  pew",
   114  	}, {
   115  		summary: "all keys with explicit local",
   116  		relid:   0,
   117  		args:    []string{"-", "u/0"},
   118  		out:     "private-address: |\n  foo: bar",
   119  	}, {
   120  		summary: "explicit smart formatting 1",
   121  		relid:   1,
   122  		unit:    "m/0",
   123  		args:    []string{"--format", "smart"},
   124  		out:     "pew: |\n  pew\n  pew",
   125  	}, {
   126  		summary: "explicit smart formatting 2",
   127  		relid:   1,
   128  		unit:    "m/0",
   129  		args:    []string{"pew", "--format", "smart"},
   130  		out:     "pew\npew\n",
   131  	}, {
   132  		summary: "explicit smart formatting 3",
   133  		relid:   1,
   134  		args:    []string{"value", "u/1", "--format", "smart"},
   135  		out:     "12345",
   136  	}, {
   137  		summary: "explicit smart formatting 4",
   138  		relid:   1,
   139  		args:    []string{"missing", "u/1", "--format", "smart"},
   140  		out:     "",
   141  	},
   142  }
   143  
   144  func (s *RelationGetSuite) TestRelationGet(c *gc.C) {
   145  	for i, t := range relationGetTests {
   146  		c.Logf("test %d: %s", i, t.summary)
   147  		hctx, _ := s.newHookContext(t.relid, t.unit, "")
   148  		com, err := jujuc.NewCommand(hctx, "relation-get")
   149  		c.Assert(err, jc.ErrorIsNil)
   150  		ctx := cmdtesting.Context(c)
   151  		code := cmd.Main(jujuc.NewJujucCommandWrappedForTest(com), ctx, t.args)
   152  		c.Check(code, gc.Equals, t.code)
   153  		if code == 0 {
   154  			c.Check(bufferString(ctx.Stderr), gc.Equals, "")
   155  			expect := t.out
   156  			if len(expect) > 0 {
   157  				expect += "\n"
   158  			}
   159  			c.Check(bufferString(ctx.Stdout), gc.Equals, expect)
   160  		} else {
   161  			c.Check(bufferString(ctx.Stdout), gc.Equals, "")
   162  			expect := fmt.Sprintf(`(.|\n)*ERROR %s\n`, t.out)
   163  			c.Check(bufferString(ctx.Stderr), gc.Matches, expect)
   164  		}
   165  	}
   166  }
   167  
   168  var relationGetFormatTests = []struct {
   169  	summary string
   170  	relid   int
   171  	unit    string
   172  	args    []string
   173  	out     interface{}
   174  }{
   175  	{
   176  		summary: "formatting 1",
   177  		relid:   1,
   178  		unit:    "m/0",
   179  		out:     map[string]interface{}{"pew": "pew\npew\n"},
   180  	}, {
   181  		summary: "formatting 2",
   182  		relid:   1,
   183  		unit:    "m/0",
   184  		args:    []string{"pew"},
   185  		out:     "pew\npew\n",
   186  	}, {
   187  		summary: "formatting 3",
   188  		relid:   1,
   189  		args:    []string{"value", "u/1"},
   190  		out:     "12345",
   191  	}, {
   192  		summary: "formatting 4",
   193  		relid:   1,
   194  		args:    []string{"missing", "u/1"},
   195  		out:     nil,
   196  	},
   197  }
   198  
   199  func (s *RelationGetSuite) TestRelationGetFormat(c *gc.C) {
   200  	testFormat := func(format string, checker gc.Checker) {
   201  		for i, t := range relationGetFormatTests {
   202  			c.Logf("test %d: %s %s", i, format, t.summary)
   203  			hctx, _ := s.newHookContext(t.relid, t.unit, "")
   204  			com, err := jujuc.NewCommand(hctx, "relation-get")
   205  			c.Assert(err, jc.ErrorIsNil)
   206  			ctx := cmdtesting.Context(c)
   207  			args := append(t.args, "--format", format)
   208  			code := cmd.Main(jujuc.NewJujucCommandWrappedForTest(com), ctx, args)
   209  			c.Check(code, gc.Equals, 0)
   210  			c.Check(bufferString(ctx.Stderr), gc.Equals, "")
   211  			stdout := bufferString(ctx.Stdout)
   212  			c.Check(stdout, checker, t.out)
   213  		}
   214  	}
   215  	testFormat("yaml", jc.YAMLEquals)
   216  	testFormat("json", jc.JSONEquals)
   217  }
   218  
   219  var helpTemplate = `
   220  Usage: %s
   221  
   222  Summary:
   223  get relation settings
   224  
   225  Options:
   226  --app  (= false)
   227      Get the relation data for the overall application, not just a unit
   228  --format  (= smart)
   229      Specify output format (json|smart|yaml)
   230  -o, --output (= "")
   231      Specify an output file
   232  -r, --relation  (= %s)
   233      Specify a relation by id
   234  
   235  Details:
   236  relation-get prints the value of a unit's relation setting, specified by key.
   237  If no key is given, or if the key is "-", all keys and values will be printed.
   238  
   239  A unit can see its own settings by calling "relation-get - MYUNIT", this will include
   240  any changes that have been made with "relation-set".
   241  
   242  When reading remote relation data, a charm can call relation-get --app - to get
   243  the data for the application data bag that is set by the remote applications
   244  leader.
   245  %s`[1:]
   246  
   247  var relationGetHelpTests = []struct {
   248  	summary string
   249  	relid   int
   250  	unit    string
   251  	usage   string
   252  	rel     string
   253  }{
   254  	{
   255  		summary: "no default relation",
   256  		relid:   -1,
   257  		usage:   "relation-get [options] <key> <unit id>",
   258  	}, {
   259  		summary: "no default unit",
   260  		relid:   1,
   261  		usage:   "relation-get [options] <key> <unit id>",
   262  		rel:     "peer1:1",
   263  	}, {
   264  		summary: "default unit",
   265  		relid:   1,
   266  		unit:    "any/1",
   267  		usage:   `relation-get [options] [<key> [<unit id>]]`,
   268  		rel:     "peer1:1",
   269  	},
   270  }
   271  
   272  func (s *RelationGetSuite) TestHelp(c *gc.C) {
   273  	for i, t := range relationGetHelpTests {
   274  		c.Logf("test %d", i)
   275  		hctx, _ := s.newHookContext(t.relid, t.unit, "")
   276  		com, err := jujuc.NewCommand(hctx, "relation-get")
   277  		c.Assert(err, jc.ErrorIsNil)
   278  		ctx := cmdtesting.Context(c)
   279  		code := cmd.Main(jujuc.NewJujucCommandWrappedForTest(com), ctx, []string{"--help"})
   280  		c.Assert(code, gc.Equals, 0)
   281  		unitHelp := ""
   282  		if t.unit != "" {
   283  			unitHelp = fmt.Sprintf("Current default unit id is %q.\n", t.unit)
   284  		}
   285  		expect := fmt.Sprintf(helpTemplate, t.usage, t.rel, unitHelp)
   286  		c.Assert(bufferString(ctx.Stdout), gc.Equals, expect)
   287  		c.Assert(bufferString(ctx.Stderr), gc.Equals, "")
   288  	}
   289  }
   290  
   291  func (s *RelationGetSuite) TestOutputPath(c *gc.C) {
   292  	hctx, _ := s.newHookContext(1, "m/0", "")
   293  	com, err := jujuc.NewCommand(hctx, "relation-get")
   294  	c.Assert(err, jc.ErrorIsNil)
   295  	ctx := cmdtesting.Context(c)
   296  	code := cmd.Main(jujuc.NewJujucCommandWrappedForTest(com), ctx, []string{"--output", "some-file", "pew"})
   297  	c.Assert(code, gc.Equals, 0)
   298  	c.Assert(bufferString(ctx.Stderr), gc.Equals, "")
   299  	c.Assert(bufferString(ctx.Stdout), gc.Equals, "")
   300  	content, err := os.ReadFile(filepath.Join(ctx.Dir, "some-file"))
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	c.Assert(string(content), gc.Equals, "pew\npew\n\n")
   303  }
   304  
   305  type relationGetInitTest struct {
   306  	summary     string
   307  	ctxrelid    int
   308  	ctxunit     string
   309  	ctxapp      string
   310  	args        []string
   311  	err         string
   312  	relid       int
   313  	key         string
   314  	unit        string
   315  	application bool
   316  }
   317  
   318  func (t relationGetInitTest) log(c *gc.C, i int) {
   319  	var summary string
   320  	if t.summary != "" {
   321  		summary = " - " + t.summary
   322  	}
   323  	c.Logf("test %d%s", i, summary)
   324  }
   325  
   326  func (t relationGetInitTest) init(c *gc.C, s *RelationGetSuite) (cmd.Command, []string) {
   327  	args := make([]string, len(t.args))
   328  	copy(args, t.args)
   329  
   330  	hctx, _ := s.newHookContext(t.ctxrelid, t.ctxunit, t.ctxapp)
   331  	com, err := jujuc.NewCommand(hctx, "relation-get")
   332  	c.Assert(err, jc.ErrorIsNil)
   333  
   334  	return com, args
   335  }
   336  
   337  func (t relationGetInitTest) check(c *gc.C, com cmd.Command, err error) {
   338  	if t.err == "" {
   339  		if !c.Check(err, jc.ErrorIsNil) {
   340  			return
   341  		}
   342  
   343  		rset := com.(*jujuc.RelationGetCommand)
   344  		c.Check(rset.RelationId, gc.Equals, t.relid)
   345  		c.Check(rset.Key, gc.Equals, t.key)
   346  		c.Check(rset.UnitOrAppName, gc.Equals, t.unit)
   347  		c.Check(rset.Application, gc.Equals, t.application)
   348  	} else {
   349  		c.Check(err, gc.ErrorMatches, t.err)
   350  	}
   351  }
   352  
   353  var relationGetInitTests = []relationGetInitTest{
   354  	{
   355  		summary:  "no relation id",
   356  		ctxrelid: -1,
   357  		err:      `no relation id specified`,
   358  	}, {
   359  		summary:  "invalid relation id",
   360  		ctxrelid: -1,
   361  		args:     []string{"-r", "one"},
   362  		err:      `invalid value "one" for option -r: invalid relation id`,
   363  	}, {
   364  		summary:  "invalid relation id with builtin context relation id",
   365  		ctxrelid: 1,
   366  		args:     []string{"-r", "one"},
   367  		err:      `invalid value "one" for option -r: invalid relation id`,
   368  	}, {
   369  		summary:  "relation not found",
   370  		ctxrelid: -1,
   371  		args:     []string{"-r", "2"},
   372  		err:      `invalid value "2" for option -r: relation not found`,
   373  	}, {
   374  		summary:  "-r overrides context relation id",
   375  		ctxrelid: 1,
   376  		ctxunit:  "u/0",
   377  		unit:     "u/0",
   378  		args:     []string{"-r", "ignored:0"},
   379  		relid:    0,
   380  	}, {
   381  		summary:  "key=value for relation-get (maybe should be invalid?)",
   382  		ctxrelid: 1,
   383  		relid:    1,
   384  		ctxunit:  "u/0",
   385  		unit:     "u/0",
   386  		args:     []string{"key=value"},
   387  		key:      "key=value",
   388  	}, {
   389  		summary:  "key supplied",
   390  		ctxrelid: 1,
   391  		relid:    1,
   392  		ctxunit:  "u/0",
   393  		unit:     "u/0",
   394  		args:     []string{"key"},
   395  		key:      "key",
   396  	}, {
   397  		summary: "magic key supplied",
   398  		ctxunit: "u/0",
   399  		unit:    "u/0",
   400  		args:    []string{"-"},
   401  		key:     "",
   402  	}, {
   403  		summary: "override ctxunit with explicit unit",
   404  		ctxunit: "u/0",
   405  		args:    []string{"key", "u/1"},
   406  		key:     "key",
   407  		unit:    "u/1",
   408  	}, {
   409  		summary: "magic key with unit",
   410  		ctxunit: "u/0",
   411  		args:    []string{"-", "u/1"},
   412  		key:     "",
   413  		unit:    "u/1",
   414  	}, {
   415  		summary:     "supply --app will use context app",
   416  		ctxunit:     "u/0",
   417  		ctxapp:      "u",
   418  		args:        []string{"--app"},
   419  		application: true,
   420  		unit:        "u",
   421  	}, {
   422  		summary:     "supply --app and app name",
   423  		ctxunit:     "u/0",
   424  		args:        []string{"--app", "-", "mysql"},
   425  		application: true,
   426  		unit:        "mysql",
   427  	}, {
   428  		summary:     "--app plus unit/0 name passes in app name",
   429  		ctxunit:     "u/0",
   430  		ctxapp:      "u",
   431  		args:        []string{"--app", "-", "mysql/0"},
   432  		application: true,
   433  		unit:        "mysql",
   434  	}, {
   435  		summary:     "--app without context app and no args is an error",
   436  		ctxapp:      "",
   437  		args:        []string{"--app"},
   438  		application: false,
   439  		err:         `no unit or application specified`,
   440  	}, {
   441  		summary:     "default with no context unit but a context app is --app",
   442  		ctxunit:     "",
   443  		ctxapp:      "u",
   444  		unit:        "u",
   445  		application: true,
   446  	}, {
   447  		summary:     "app name in context but overridden by args",
   448  		ctxunit:     "",
   449  		ctxapp:      "u",
   450  		unit:        "mysql/0",
   451  		args:        []string{"-", "mysql/0"},
   452  		application: false,
   453  	}, {
   454  		summary: "extra arguments",
   455  		ctxunit: "u/0",
   456  		ctxapp:  "u",
   457  		args:    []string{"-", "--app", "mysql", "args"},
   458  		err:     `unrecognized args: \["args"\]`,
   459  	}, {
   460  		summary: "app name in context but overridden by app args",
   461  		ctxunit: "",
   462  		ctxapp:  "u",
   463  		unit:    "mysql",
   464  		args:    []string{"-", "--app", "mysql"},
   465  		// This doesn't get auto set if we didn't pull it from the context
   466  		application: true,
   467  	}, {
   468  		summary: "application name with no --app",
   469  		ctxunit: "u/0",
   470  		ctxapp:  "u",
   471  		args:    []string{"-", "mysql"},
   472  		err:     `expected unit name, got application name "mysql"`,
   473  	}, {
   474  		summary: "invalid unit name",
   475  		ctxunit: "u/0",
   476  		ctxapp:  "u",
   477  		args:    []string{"-", "unit//0"},
   478  		err:     `invalid unit name "unit//0"`,
   479  	},
   480  }
   481  
   482  func (s *RelationGetSuite) TestInit(c *gc.C) {
   483  	for i, t := range relationGetInitTests {
   484  		t.log(c, i)
   485  		com, args := t.init(c, s)
   486  
   487  		err := cmdtesting.InitCommand(com, args)
   488  		t.check(c, com, err)
   489  	}
   490  }