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 }