github.com/marwan-at-work/consul@v1.4.5/command/lock/lock_test.go (about) 1 package lock 2 3 import ( 4 "io/ioutil" 5 "path/filepath" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/hashicorp/consul/agent" 11 "github.com/hashicorp/consul/api" 12 "github.com/hashicorp/consul/testrpc" 13 "github.com/mitchellh/cli" 14 ) 15 16 func argFail(t *testing.T, args []string, expected string) { 17 ui := cli.NewMockUi() 18 c := New(ui) 19 c.flags.SetOutput(ui.ErrorWriter) 20 if code := c.Run(args); code != 1 { 21 t.Fatalf("expected return code 1, got %d", code) 22 } 23 if reason := ui.ErrorWriter.String(); !strings.Contains(reason, expected) { 24 t.Fatalf("bad reason: got='%s', expected='%s'", reason, expected) 25 } 26 27 } 28 29 func TestLockCommand_noTabs(t *testing.T) { 30 t.Parallel() 31 if strings.ContainsRune(New(cli.NewMockUi()).Help(), '\t') { 32 t.Fatal("help has tabs") 33 } 34 } 35 36 func TestLockCommand_BadArgs(t *testing.T) { 37 t.Parallel() 38 argFail(t, []string{"-try=blah", "test/prefix", "date"}, "invalid duration") 39 argFail(t, []string{"-try=-10s", "test/prefix", "date"}, "Timeout must be positive") 40 argFail(t, []string{"-monitor-retry=-5", "test/prefix", "date"}, "must be >= 0") 41 } 42 43 func TestLockCommand(t *testing.T) { 44 t.Parallel() 45 a := agent.NewTestAgent(t, t.Name(), ``) 46 defer a.Shutdown() 47 48 testrpc.WaitForLeader(t, a.RPC, "dc1") 49 50 ui := cli.NewMockUi() 51 c := New(ui) 52 53 filePath := filepath.Join(a.Config.DataDir, "test_touch") 54 args := []string{"-http-addr=" + a.HTTPAddr(), "test/prefix", "touch", filePath} 55 56 code := c.Run(args) 57 if code != 0 { 58 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 59 } 60 61 // Check for the file 62 _, err := ioutil.ReadFile(filePath) 63 if err != nil { 64 t.Fatalf("err: %v", err) 65 } 66 } 67 68 func TestLockCommand_NoShell(t *testing.T) { 69 t.Parallel() 70 a := agent.NewTestAgent(t, t.Name(), ``) 71 defer a.Shutdown() 72 73 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 74 75 ui := cli.NewMockUi() 76 c := New(ui) 77 78 filePath := filepath.Join(a.Config.DataDir, "test_touch") 79 args := []string{"-http-addr=" + a.HTTPAddr(), "-shell=false", "test/prefix", "touch", filePath} 80 81 code := c.Run(args) 82 if code != 0 { 83 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 84 } 85 86 // Check for the file 87 _, err := ioutil.ReadFile(filePath) 88 if err != nil { 89 t.Fatalf("err: %v", err) 90 } 91 } 92 93 func TestLockCommand_TryLock(t *testing.T) { 94 t.Parallel() 95 a := agent.NewTestAgent(t, t.Name(), ``) 96 defer a.Shutdown() 97 98 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 99 100 ui := cli.NewMockUi() 101 c := New(ui) 102 103 filePath := filepath.Join(a.Config.DataDir, "test_touch") 104 args := []string{"-http-addr=" + a.HTTPAddr(), "-try=10s", "test/prefix", "touch", filePath} 105 106 // Run the command. 107 var lu *LockUnlock 108 code := c.run(args, &lu) 109 if code != 0 { 110 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 111 } 112 _, err := ioutil.ReadFile(filePath) 113 if err != nil { 114 t.Fatalf("err: %v", err) 115 } 116 117 // Make sure the try options were set correctly. 118 opts, ok := lu.rawOpts.(*api.LockOptions) 119 if !ok { 120 t.Fatalf("bad type") 121 } 122 if !opts.LockTryOnce || opts.LockWaitTime != 10*time.Second { 123 t.Fatalf("bad: %#v", opts) 124 } 125 } 126 127 func TestLockCommand_TrySemaphore(t *testing.T) { 128 t.Parallel() 129 a := agent.NewTestAgent(t, t.Name(), ``) 130 defer a.Shutdown() 131 132 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 133 134 ui := cli.NewMockUi() 135 c := New(ui) 136 137 filePath := filepath.Join(a.Config.DataDir, "test_touch") 138 args := []string{"-http-addr=" + a.HTTPAddr(), "-n=3", "-try=10s", "test/prefix", "touch", filePath} 139 140 // Run the command. 141 var lu *LockUnlock 142 code := c.run(args, &lu) 143 if code != 0 { 144 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 145 } 146 _, err := ioutil.ReadFile(filePath) 147 if err != nil { 148 t.Fatalf("err: %v", err) 149 } 150 151 // Make sure the try options were set correctly. 152 opts, ok := lu.rawOpts.(*api.SemaphoreOptions) 153 if !ok { 154 t.Fatalf("bad type") 155 } 156 if !opts.SemaphoreTryOnce || opts.SemaphoreWaitTime != 10*time.Second { 157 t.Fatalf("bad: %#v", opts) 158 } 159 } 160 161 func TestLockCommand_MonitorRetry_Lock_Default(t *testing.T) { 162 t.Parallel() 163 a := agent.NewTestAgent(t, t.Name(), ``) 164 defer a.Shutdown() 165 166 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 167 168 ui := cli.NewMockUi() 169 c := New(ui) 170 171 filePath := filepath.Join(a.Config.DataDir, "test_touch") 172 args := []string{"-http-addr=" + a.HTTPAddr(), "test/prefix", "touch", filePath} 173 174 // Run the command. 175 var lu *LockUnlock 176 code := c.run(args, &lu) 177 if code != 0 { 178 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 179 } 180 _, err := ioutil.ReadFile(filePath) 181 if err != nil { 182 t.Fatalf("err: %v", err) 183 } 184 185 // Make sure the monitor options were set correctly. 186 opts, ok := lu.rawOpts.(*api.LockOptions) 187 if !ok { 188 t.Fatalf("bad type") 189 } 190 if opts.MonitorRetries != defaultMonitorRetry || 191 opts.MonitorRetryTime != defaultMonitorRetryTime { 192 t.Fatalf("bad: %#v", opts) 193 } 194 } 195 196 func TestLockCommand_MonitorRetry_Semaphore_Default(t *testing.T) { 197 t.Parallel() 198 a := agent.NewTestAgent(t, t.Name(), ``) 199 defer a.Shutdown() 200 201 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 202 203 ui := cli.NewMockUi() 204 c := New(ui) 205 206 filePath := filepath.Join(a.Config.DataDir, "test_touch") 207 args := []string{"-http-addr=" + a.HTTPAddr(), "-n=3", "test/prefix", "touch", filePath} 208 209 // Run the command. 210 var lu *LockUnlock 211 code := c.run(args, &lu) 212 if code != 0 { 213 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 214 } 215 _, err := ioutil.ReadFile(filePath) 216 if err != nil { 217 t.Fatalf("err: %v", err) 218 } 219 220 // Make sure the monitor options were set correctly. 221 opts, ok := lu.rawOpts.(*api.SemaphoreOptions) 222 if !ok { 223 t.Fatalf("bad type") 224 } 225 if opts.MonitorRetries != defaultMonitorRetry || 226 opts.MonitorRetryTime != defaultMonitorRetryTime { 227 t.Fatalf("bad: %#v", opts) 228 } 229 } 230 231 func TestLockCommand_MonitorRetry_Lock_Arg(t *testing.T) { 232 t.Parallel() 233 a := agent.NewTestAgent(t, t.Name(), ``) 234 defer a.Shutdown() 235 236 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 237 238 ui := cli.NewMockUi() 239 c := New(ui) 240 241 filePath := filepath.Join(a.Config.DataDir, "test_touch") 242 args := []string{"-http-addr=" + a.HTTPAddr(), "-monitor-retry=9", "test/prefix", "touch", filePath} 243 244 // Run the command. 245 var lu *LockUnlock 246 code := c.run(args, &lu) 247 if code != 0 { 248 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 249 } 250 _, err := ioutil.ReadFile(filePath) 251 if err != nil { 252 t.Fatalf("err: %v", err) 253 } 254 255 // Make sure the monitor options were set correctly. 256 opts, ok := lu.rawOpts.(*api.LockOptions) 257 if !ok { 258 t.Fatalf("bad type") 259 } 260 if opts.MonitorRetries != 9 || 261 opts.MonitorRetryTime != defaultMonitorRetryTime { 262 t.Fatalf("bad: %#v", opts) 263 } 264 } 265 266 func TestLockCommand_MonitorRetry_Semaphore_Arg(t *testing.T) { 267 t.Parallel() 268 a := agent.NewTestAgent(t, t.Name(), ``) 269 defer a.Shutdown() 270 271 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 272 273 ui := cli.NewMockUi() 274 c := New(ui) 275 276 filePath := filepath.Join(a.Config.DataDir, "test_touch") 277 args := []string{"-http-addr=" + a.HTTPAddr(), "-n=3", "-monitor-retry=9", "test/prefix", "touch", filePath} 278 279 // Run the command. 280 var lu *LockUnlock 281 code := c.run(args, &lu) 282 if code != 0 { 283 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 284 } 285 _, err := ioutil.ReadFile(filePath) 286 if err != nil { 287 t.Fatalf("err: %v", err) 288 } 289 290 // Make sure the monitor options were set correctly. 291 opts, ok := lu.rawOpts.(*api.SemaphoreOptions) 292 if !ok { 293 t.Fatalf("bad type") 294 } 295 if opts.MonitorRetries != 9 || 296 opts.MonitorRetryTime != defaultMonitorRetryTime { 297 t.Fatalf("bad: %#v", opts) 298 } 299 } 300 301 func TestLockCommand_ChildExitCode(t *testing.T) { 302 t.Parallel() 303 a := agent.NewTestAgent(t, t.Name(), ``) 304 defer a.Shutdown() 305 306 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 307 308 tt := []struct { 309 name string 310 args []string 311 want int 312 }{ 313 { 314 name: "clean exit", 315 args: []string{"-http-addr=" + a.HTTPAddr(), "-child-exit-code", "test/prefix", "sh", "-c", "exit", "0"}, 316 want: 0, 317 }, 318 { 319 name: "error exit", 320 args: []string{"-http-addr=" + a.HTTPAddr(), "-child-exit-code", "test/prefix", "exit", "1"}, 321 want: 2, 322 }, 323 { 324 name: "not propagated", 325 args: []string{"-http-addr=" + a.HTTPAddr(), "test/prefix", "sh", "-c", "exit", "1"}, 326 want: 0, 327 }, 328 } 329 330 for _, tc := range tt { 331 t.Run(tc.name, func(t *testing.T) { 332 ui := cli.NewMockUi() 333 c := New(ui) 334 335 if got := c.Run(tc.args); got != tc.want { 336 t.Fatalf("got %d want %d", got, tc.want) 337 } 338 }) 339 } 340 }