github.com/hashicorp/packer@v1.14.3/packer/provisioner_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package packer 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "testing" 11 "time" 12 13 packersdk "github.com/hashicorp/packer-plugin-sdk/packer" 14 ) 15 16 func TestProvisionHook_Impl(t *testing.T) { 17 var raw interface{} 18 raw = &ProvisionHook{} 19 if _, ok := raw.(packersdk.Hook); !ok { 20 t.Fatalf("must be a Hook") 21 } 22 } 23 24 func TestProvisionHook(t *testing.T) { 25 pA := &packersdk.MockProvisioner{} 26 pB := &packersdk.MockProvisioner{} 27 28 ui := testUi() 29 var comm packersdk.Communicator = new(packersdk.MockCommunicator) 30 var data interface{} = nil 31 32 hook := &ProvisionHook{ 33 Provisioners: []*HookedProvisioner{ 34 {pA, nil, ""}, 35 {pB, nil, ""}, 36 }, 37 } 38 39 hook.Run(context.Background(), "foo", ui, comm, data) 40 41 if !pA.ProvCalled { 42 t.Error("provision should be called on pA") 43 } 44 45 if !pB.ProvCalled { 46 t.Error("provision should be called on pB") 47 } 48 } 49 50 func TestProvisionHook_nilComm(t *testing.T) { 51 pA := &packersdk.MockProvisioner{} 52 pB := &packersdk.MockProvisioner{} 53 54 ui := testUi() 55 var comm packersdk.Communicator = nil 56 var data interface{} = nil 57 58 hook := &ProvisionHook{ 59 Provisioners: []*HookedProvisioner{ 60 {pA, nil, ""}, 61 {pB, nil, ""}, 62 }, 63 } 64 65 err := hook.Run(context.Background(), "foo", ui, comm, data) 66 if err == nil { 67 t.Fatal("should error") 68 } 69 } 70 71 func TestProvisionHook_cancel(t *testing.T) { 72 topCtx, topCtxCancel := context.WithCancel(context.Background()) 73 74 p := &packersdk.MockProvisioner{ 75 ProvFunc: func(ctx context.Context) error { 76 topCtxCancel() 77 <-ctx.Done() 78 return ctx.Err() 79 }, 80 } 81 82 hook := &ProvisionHook{ 83 Provisioners: []*HookedProvisioner{ 84 {p, nil, ""}, 85 }, 86 } 87 88 err := hook.Run(topCtx, "foo", nil, new(packersdk.MockCommunicator), nil) 89 if err == nil { 90 t.Fatal("should have err") 91 } 92 } 93 94 // TODO(mitchellh): Test that they're run in the proper order 95 96 func TestPausedProvisioner_impl(t *testing.T) { 97 var _ packersdk.Provisioner = new(PausedProvisioner) 98 } 99 100 func TestPausedProvisionerPrepare(t *testing.T) { 101 mock := new(packersdk.MockProvisioner) 102 prov := &PausedProvisioner{ 103 Provisioner: mock, 104 } 105 106 prov.Prepare(42) 107 if !mock.PrepCalled { 108 t.Fatal("prepare should be called") 109 } 110 if mock.PrepConfigs[0] != 42 { 111 t.Fatal("should have proper configs") 112 } 113 } 114 115 func TestPausedProvisionerProvision(t *testing.T) { 116 mock := new(packersdk.MockProvisioner) 117 prov := &PausedProvisioner{ 118 Provisioner: mock, 119 } 120 121 ui := testUi() 122 comm := new(packersdk.MockCommunicator) 123 prov.Provision(context.Background(), ui, comm, make(map[string]interface{})) 124 if !mock.ProvCalled { 125 t.Fatal("prov should be called") 126 } 127 if mock.ProvUi != ui { 128 t.Fatal("should have proper ui") 129 } 130 if mock.ProvCommunicator != comm { 131 t.Fatal("should have proper comm") 132 } 133 } 134 135 func TestPausedProvisionerProvision_waits(t *testing.T) { 136 startTime := time.Now() 137 waitTime := 50 * time.Millisecond 138 139 prov := &PausedProvisioner{ 140 PauseBefore: waitTime, 141 Provisioner: &packersdk.MockProvisioner{ 142 ProvFunc: func(context.Context) error { 143 timeSinceStartTime := time.Since(startTime) 144 if timeSinceStartTime < waitTime { 145 return fmt.Errorf("Spent not enough time waiting: %s", timeSinceStartTime) 146 } 147 return nil 148 }, 149 }, 150 } 151 152 err := prov.Provision(context.Background(), testUi(), new(packersdk.MockCommunicator), make(map[string]interface{})) 153 154 if err != nil { 155 t.Fatalf("prov failed: %v", err) 156 } 157 } 158 159 func TestPausedProvisionerCancel(t *testing.T) { 160 topCtx, cancelTopCtx := context.WithCancel(context.Background()) 161 162 mock := new(packersdk.MockProvisioner) 163 prov := &PausedProvisioner{ 164 Provisioner: mock, 165 } 166 167 mock.ProvFunc = func(ctx context.Context) error { 168 cancelTopCtx() 169 <-ctx.Done() 170 return ctx.Err() 171 } 172 173 err := prov.Provision(topCtx, testUi(), new(packersdk.MockCommunicator), make(map[string]interface{})) 174 if err == nil { 175 t.Fatal("should have err") 176 } 177 } 178 179 func TestDebuggedProvisioner_impl(t *testing.T) { 180 var _ packersdk.Provisioner = new(DebuggedProvisioner) 181 } 182 183 func TestDebuggedProvisionerPrepare(t *testing.T) { 184 mock := new(packersdk.MockProvisioner) 185 prov := &DebuggedProvisioner{ 186 Provisioner: mock, 187 } 188 189 prov.Prepare(42) 190 if !mock.PrepCalled { 191 t.Fatal("prepare should be called") 192 } 193 if mock.PrepConfigs[0] != 42 { 194 t.Fatal("should have proper configs") 195 } 196 } 197 198 func TestDebuggedProvisionerProvision(t *testing.T) { 199 mock := new(packersdk.MockProvisioner) 200 prov := &DebuggedProvisioner{ 201 Provisioner: mock, 202 } 203 204 ui := testUi() 205 comm := new(packersdk.MockCommunicator) 206 writeReader(ui, "\n") 207 prov.Provision(context.Background(), ui, comm, make(map[string]interface{})) 208 if !mock.ProvCalled { 209 t.Fatal("prov should be called") 210 } 211 if mock.ProvUi != ui { 212 t.Fatal("should have proper ui") 213 } 214 if mock.ProvCommunicator != comm { 215 t.Fatal("should have proper comm") 216 } 217 } 218 219 func TestDebuggedProvisionerCancel(t *testing.T) { 220 topCtx, topCtxCancel := context.WithCancel(context.Background()) 221 222 mock := new(packersdk.MockProvisioner) 223 prov := &DebuggedProvisioner{ 224 Provisioner: mock, 225 } 226 227 mock.ProvFunc = func(ctx context.Context) error { 228 topCtxCancel() 229 <-ctx.Done() 230 return ctx.Err() 231 } 232 233 err := prov.Provision(topCtx, testUi(), new(packersdk.MockCommunicator), make(map[string]interface{})) 234 if err == nil { 235 t.Fatal("should have error") 236 } 237 } 238 239 func TestRetriedProvisioner_impl(t *testing.T) { 240 var _ packersdk.Provisioner = new(RetriedProvisioner) 241 } 242 243 func TestRetriedProvisionerPrepare(t *testing.T) { 244 mock := new(packersdk.MockProvisioner) 245 prov := &RetriedProvisioner{ 246 Provisioner: mock, 247 } 248 249 err := prov.Prepare(42) 250 if err != nil { 251 t.Fatal("should not have errored") 252 } 253 if !mock.PrepCalled { 254 t.Fatal("prepare should be called") 255 } 256 if mock.PrepConfigs[0] != 42 { 257 t.Fatal("should have proper configs") 258 } 259 } 260 261 func TestRetriedProvisionerProvision(t *testing.T) { 262 mock := &packersdk.MockProvisioner{ 263 ProvFunc: func(ctx context.Context) error { 264 return errors.New("failed") 265 }, 266 } 267 268 prov := &RetriedProvisioner{ 269 MaxRetries: 2, 270 Provisioner: mock, 271 } 272 273 ui := testUi() 274 comm := new(packersdk.MockCommunicator) 275 err := prov.Provision(context.Background(), ui, comm, make(map[string]interface{})) 276 if err != nil { 277 t.Fatal("should not have errored") 278 } 279 if !mock.ProvCalled { 280 t.Fatal("prov should be called") 281 } 282 if !mock.ProvRetried { 283 t.Fatal("prov should be retried") 284 } 285 if mock.ProvUi != ui { 286 t.Fatal("should have proper ui") 287 } 288 if mock.ProvCommunicator != comm { 289 t.Fatal("should have proper comm") 290 } 291 } 292 293 func TestRetriedProvisionerCancelledProvision(t *testing.T) { 294 // Don't retry if context is cancelled 295 ctx, topCtxCancel := context.WithCancel(context.Background()) 296 297 mock := &packersdk.MockProvisioner{ 298 ProvFunc: func(ctx context.Context) error { 299 topCtxCancel() 300 <-ctx.Done() 301 return ctx.Err() 302 }, 303 } 304 305 prov := &RetriedProvisioner{ 306 MaxRetries: 2, 307 Provisioner: mock, 308 } 309 310 ui := testUi() 311 comm := new(packersdk.MockCommunicator) 312 err := prov.Provision(ctx, ui, comm, make(map[string]interface{})) 313 if err == nil { 314 t.Fatal("should have errored") 315 } 316 if !mock.ProvCalled { 317 t.Fatal("prov should be called") 318 } 319 if mock.ProvRetried { 320 t.Fatal("prov should NOT be retried") 321 } 322 if mock.ProvUi != ui { 323 t.Fatal("should have proper ui") 324 } 325 if mock.ProvCommunicator != comm { 326 t.Fatal("should have proper comm") 327 } 328 } 329 330 func TestRetriedProvisionerCancel(t *testing.T) { 331 topCtx, cancelTopCtx := context.WithCancel(context.Background()) 332 333 mock := new(packersdk.MockProvisioner) 334 prov := &RetriedProvisioner{ 335 Provisioner: mock, 336 } 337 338 mock.ProvFunc = func(ctx context.Context) error { 339 cancelTopCtx() 340 <-ctx.Done() 341 return ctx.Err() 342 } 343 344 err := prov.Provision(topCtx, testUi(), new(packersdk.MockCommunicator), make(map[string]interface{})) 345 if err == nil { 346 t.Fatal("should have err") 347 } 348 }