packer-cn/command/build_parallel_test.go

174 lines
4.3 KiB
Go
Raw Normal View History

2019-04-19 03:10:48 -04:00
package command
import (
"bytes"
"context"
"fmt"
"path/filepath"
"sync"
"testing"
"github.com/hashicorp/hcl/v2/hcldec"
2019-05-02 08:41:44 -04:00
"golang.org/x/sync/errgroup"
2020-12-17 16:29:25 -05:00
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer/builder/file"
2019-04-19 03:10:48 -04:00
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/provisioner/sleep"
2019-04-19 03:10:48 -04:00
)
2019-05-02 09:04:02 -04:00
// NewParallelTestBuilder will return a New ParallelTestBuilder that will
// unlock after `runs` builds
2019-05-02 08:41:44 -04:00
func NewParallelTestBuilder(runs int) *ParallelTestBuilder {
2019-05-02 09:04:02 -04:00
pb := &ParallelTestBuilder{}
2019-05-02 08:41:44 -04:00
pb.wg.Add(runs)
return pb
}
// The ParallelTestBuilder's first run will lock
2019-04-19 03:10:48 -04:00
type ParallelTestBuilder struct {
2019-05-02 08:41:44 -04:00
wg sync.WaitGroup
2019-04-19 03:10:48 -04:00
}
func (b *ParallelTestBuilder) ConfigSpec() hcldec.ObjectSpec { return nil }
func (b *ParallelTestBuilder) Prepare(raws ...interface{}) ([]string, []string, error) {
return nil, nil, nil
}
2019-04-19 03:10:48 -04:00
func (b *ParallelTestBuilder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
2019-05-02 08:41:44 -04:00
ui.Say("building")
2019-04-19 03:10:48 -04:00
b.wg.Done()
return nil, nil
}
2019-05-02 09:04:02 -04:00
// LockedBuilder wont run until unlock is called
type LockedBuilder struct{ unlock chan interface{} }
func (b *LockedBuilder) ConfigSpec() hcldec.ObjectSpec { return nil }
2020-03-17 07:05:37 -04:00
func (b *LockedBuilder) Prepare(raws ...interface{}) ([]string, []string, error) {
return nil, nil, nil
}
2019-05-02 09:04:02 -04:00
func (b *LockedBuilder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
2019-05-02 09:04:02 -04:00
ui.Say("locking build")
select {
case <-b.unlock:
case <-ctx.Done():
return nil, ctx.Err()
}
return nil, nil
}
2019-04-19 03:10:48 -04:00
// testMetaFile creates a Meta object that includes a file builder
2019-05-02 09:04:02 -04:00
func testMetaParallel(t *testing.T, builder *ParallelTestBuilder, locked *LockedBuilder) Meta {
2019-04-19 03:10:48 -04:00
var out, err bytes.Buffer
return Meta{
CoreConfig: &packer.CoreConfig{
Components: packer.ComponentFinder{
BuilderStore: packer.MapOfBuilder{
2020-12-01 16:42:11 -05:00
"parallel-test": func() (packersdk.Builder, error) { return builder, nil },
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
"lock": func() (packersdk.Builder, error) { return locked, nil },
2019-04-19 03:10:48 -04:00
},
ProvisionerStore: packer.MapOfProvisioner{
2020-12-01 17:25:14 -05:00
"sleep": func() (packersdk.Provisioner, error) { return &sleep.Provisioner{}, nil },
},
2019-04-19 03:10:48 -04:00
},
},
Ui: &packersdk.BasicUi{
2019-04-19 03:10:48 -04:00
Writer: &out,
ErrorWriter: &err,
},
}
}
2019-05-02 09:04:02 -04:00
func TestBuildParallel_1(t *testing.T) {
// testfile has 6 builds, with first one locks 'forever', other builds
// should go through.
2019-05-02 08:41:44 -04:00
b := NewParallelTestBuilder(5)
2019-05-02 09:04:02 -04:00
locked := &LockedBuilder{unlock: make(chan interface{})}
2019-04-19 03:10:48 -04:00
c := &BuildCommand{
2019-05-02 09:04:02 -04:00
Meta: testMetaParallel(t, b, locked),
2019-04-19 03:10:48 -04:00
}
args := []string{
2020-05-08 11:46:33 -04:00
fmt.Sprintf("-parallel-builds=10"),
filepath.Join(testFixture("parallel"), "1lock-5wg.json"),
2019-04-19 03:10:48 -04:00
}
2019-05-02 08:41:44 -04:00
wg := errgroup.Group{}
wg.Go(func() error {
2019-04-19 03:10:48 -04:00
if code := c.Run(args); code != 0 {
fatalCommand(t, c.Meta)
}
2019-05-02 08:41:44 -04:00
return nil
})
2019-04-19 03:10:48 -04:00
2019-05-02 09:04:02 -04:00
b.wg.Wait() // ran 5 times
close(locked.unlock) // unlock locking one
wg.Wait() // wait for termination
2019-04-19 03:10:48 -04:00
}
func TestBuildParallel_2(t *testing.T) {
// testfile has 6 builds, 2 of them lock 'forever', other builds
// should go through.
b := NewParallelTestBuilder(4)
locked := &LockedBuilder{unlock: make(chan interface{})}
c := &BuildCommand{
Meta: testMetaParallel(t, b, locked),
}
args := []string{
fmt.Sprintf("-parallel-builds=3"),
2019-05-06 09:47:53 -04:00
filepath.Join(testFixture("parallel"), "2lock-4wg.json"),
}
wg := errgroup.Group{}
wg.Go(func() error {
if code := c.Run(args); code != 0 {
fatalCommand(t, c.Meta)
}
return nil
})
b.wg.Wait() // ran 4 times
close(locked.unlock) // unlock locking one
wg.Wait() // wait for termination
}
func TestBuildParallel_Timeout(t *testing.T) {
// testfile has 6 builds, 1 of them locks 'forever', one locks and times
// out other builds should go through.
b := NewParallelTestBuilder(4)
locked := &LockedBuilder{unlock: make(chan interface{})}
c := &BuildCommand{
Meta: testMetaParallel(t, b, locked),
}
args := []string{
fmt.Sprintf("-parallel-builds=3"),
filepath.Join(testFixture("parallel"), "2lock-timeout.json"),
}
wg := errgroup.Group{}
wg.Go(func() error {
if code := c.Run(args); code == 0 {
fatalCommand(t, c.Meta)
}
return nil
})
b.wg.Wait() // ran 4 times
close(locked.unlock) // unlock locking one
wg.Wait() // wait for termination
}