Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose parallelism semaphore in the cli ui as -parallelism=# #1834

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions command/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func (c *ApplyCommand) Run(args []string) int {
cmdFlags.BoolVar(&destroyForce, "force", false, "force")
}
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", 0, "parallelism")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
Expand Down Expand Up @@ -93,9 +94,10 @@ func (c *ApplyCommand) Run(args []string) int {

// Build the context based on the arguments given
ctx, planned, err := c.Context(contextOpts{
Destroy: c.Destroy,
Path: configPath,
StatePath: c.Meta.statePath,
Destroy: c.Destroy,
Path: configPath,
StatePath: c.Meta.statePath,
Parallelism: c.Meta.parallelism,
})
if err != nil {
c.Ui.Error(err.Error())
Expand Down Expand Up @@ -305,6 +307,8 @@ Options:

-no-color If specified, output won't contain any color.

-parallelism=# Limit the number of concurrent operations.

-refresh=true Update state prior to checking for differences. This
has no effect if a plan file is given to apply.

Expand Down
76 changes: 76 additions & 0 deletions command/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,82 @@ func TestApply(t *testing.T) {
}
}

func TestApply_parallelism1(t *testing.T) {
statePath := testTempFile(t)

ui := new(cli.MockUi)
p := testProvider()
pr := new(terraform.MockResourceProvisioner)

pr.ApplyFn = func(*terraform.InstanceState, *terraform.ResourceConfig) error {
time.Sleep(time.Second)
return nil
}

args := []string{
"-state", statePath,
"-parallelism=1",
testFixturePath("parallelism"),
}

c := &ApplyCommand{
Meta: Meta{
ContextOpts: testCtxConfigWithShell(p, pr),
Ui: ui,
},
}

start := time.Now()
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
elapsed := time.Since(start).Seconds()

// This test should take exactly two seconds, plus some minor amount of execution time.
if elapsed < 2 || elapsed > 2.2 {
t.Fatalf("bad: %f\n\n%s", elapsed, ui.ErrorWriter.String())
}

}

func TestApply_parallelism2(t *testing.T) {
statePath := testTempFile(t)

ui := new(cli.MockUi)
p := testProvider()
pr := new(terraform.MockResourceProvisioner)

pr.ApplyFn = func(*terraform.InstanceState, *terraform.ResourceConfig) error {
time.Sleep(time.Second)
return nil
}

args := []string{
"-state", statePath,
"-parallelism=2",
testFixturePath("parallelism"),
}

c := &ApplyCommand{
Meta: Meta{
ContextOpts: testCtxConfigWithShell(p, pr),
Ui: ui,
},
}

start := time.Now()
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
elapsed := time.Since(start).Seconds()

// This test should take exactly one second, plus some minor amount of execution time.
if elapsed < 1 || elapsed > 1.2 {
t.Fatalf("bad: %f\n\n%s", elapsed, ui.ErrorWriter.String())
}

}

func TestApply_configInvalid(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
Expand Down
15 changes: 15 additions & 0 deletions command/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ func testCtxConfig(p terraform.ResourceProvider) *terraform.ContextOpts {
}
}

func testCtxConfigWithShell(p terraform.ResourceProvider, pr terraform.ResourceProvisioner) *terraform.ContextOpts {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I duplicated the existing testCtxConfig function and added in the shell provisioner so I could determine from execution time whether or not the parallelism flag is functioning. Seems like I could DRY this up a bit, but this works for now.

return &terraform.ContextOpts{
Providers: map[string]terraform.ResourceProviderFactory{
"test": func() (terraform.ResourceProvider, error) {
return p, nil
},
},
Provisioners: map[string]terraform.ResourceProvisionerFactory{
"shell": func() (terraform.ResourceProvisioner, error) {
return pr, nil
},
},
}
}

func testModule(t *testing.T, name string) *module.Tree {
mod, err := module.NewTreeModule("", filepath.Join(fixtureDir, name))
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions command/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,13 @@ type Meta struct {
//
// backupPath is used to backup the state file before writing a modified
// version. It defaults to stateOutPath + DefaultBackupExtention
//
// parallelism is used to control the number of concurrent operations
// allowed when walking the graph
statePath string
stateOutPath string
backupPath string
parallelism int
}

// initStatePaths is used to initialize the default values for
Expand Down Expand Up @@ -151,6 +155,7 @@ func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) {
}

opts.Module = mod
opts.Parallelism = copts.Parallelism
opts.State = state.State()
ctx := terraform.NewContext(opts)
return ctx, false, nil
Expand Down Expand Up @@ -429,4 +434,7 @@ type contextOpts struct {

// Set to true when running a destroy plan/apply.
Destroy bool

// Number of concurrent operations allowed
Parallelism int
}
8 changes: 5 additions & 3 deletions command/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func (c *PlanCommand) Run(args []string) int {
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
c.addModuleDepthFlag(cmdFlags, &moduleDepth)
cmdFlags.StringVar(&outPath, "out", "", "path")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", 0, "parallelism")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode")
Expand Down Expand Up @@ -54,9 +55,10 @@ func (c *PlanCommand) Run(args []string) int {
}

ctx, _, err := c.Context(contextOpts{
Destroy: destroy,
Path: path,
StatePath: c.Meta.statePath,
Destroy: destroy,
Path: path,
StatePath: c.Meta.statePath,
Parallelism: c.Meta.parallelism,
})
if err != nil {
c.Ui.Error(err.Error())
Expand Down
6 changes: 4 additions & 2 deletions command/refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func (c *RefreshCommand) Run(args []string) int {

cmdFlags := c.Meta.flagSet("refresh")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", 0, "parallelism")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
Expand Down Expand Up @@ -78,8 +79,9 @@ func (c *RefreshCommand) Run(args []string) int {

// Build the context based on the arguments given
ctx, _, err := c.Context(contextOpts{
Path: configPath,
StatePath: c.Meta.statePath,
Path: configPath,
StatePath: c.Meta.statePath,
Parallelism: c.Meta.parallelism,
})
if err != nil {
c.Ui.Error(err.Error())
Expand Down
13 changes: 13 additions & 0 deletions command/test-fixtures/parallelism/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
resource "test_instance" "foo1" {
ami = "bar"

// shell has been configured to sleep for one second
provisioner "shell" {}
}

resource "test_instance" "foo2" {
ami = "bar"

// shell has been configured to sleep for one second
provisioner "shell" {}
}