Merge pull request #3365 from josephholsten/f-add-parallelism-to-ui

add parallelism to ui
This commit is contained in:
Paul Hinze 2015-10-05 14:43:59 -05:00
commit 18125d84d2
7 changed files with 128 additions and 8 deletions

View File

@ -39,6 +39,7 @@ func (c *ApplyCommand) Run(args []string) int {
cmdFlags.BoolVar(&destroyForce, "force", false, "force") cmdFlags.BoolVar(&destroyForce, "force", false, "force")
} }
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") 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.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
@ -94,9 +95,10 @@ func (c *ApplyCommand) Run(args []string) int {
// Build the context based on the arguments given // Build the context based on the arguments given
ctx, planned, err := c.Context(contextOpts{ ctx, planned, err := c.Context(contextOpts{
Destroy: c.Destroy, Destroy: c.Destroy,
Path: configPath, Path: configPath,
StatePath: c.Meta.statePath, StatePath: c.Meta.statePath,
Parallelism: c.Meta.parallelism,
}) })
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())
@ -278,6 +280,8 @@ Options:
-no-color If specified, output won't contain any color. -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 -refresh=true Update state prior to checking for differences. This
has no effect if a plan file is given to apply. has no effect if a plan file is given to apply.

View File

@ -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) { func TestApply_configInvalid(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)

View File

@ -52,6 +52,21 @@ func testCtxConfig(p terraform.ResourceProvider) *terraform.ContextOpts {
} }
} }
func testCtxConfigWithShell(p terraform.ResourceProvider, pr terraform.ResourceProvisioner) *terraform.ContextOpts {
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 { func testModule(t *testing.T, name string) *module.Tree {
mod, err := module.NewTreeModule("", filepath.Join(fixtureDir, name)) mod, err := module.NewTreeModule("", filepath.Join(fixtureDir, name))
if err != nil { if err != nil {

View File

@ -59,9 +59,13 @@ type Meta struct {
// //
// backupPath is used to backup the state file before writing a modified // backupPath is used to backup the state file before writing a modified
// version. It defaults to stateOutPath + DefaultBackupExtension // version. It defaults to stateOutPath + DefaultBackupExtension
//
// parallelism is used to control the number of concurrent operations
// allowed when walking the graph
statePath string statePath string
stateOutPath string stateOutPath string
backupPath string backupPath string
parallelism int
} }
// initStatePaths is used to initialize the default values for // initStatePaths is used to initialize the default values for
@ -151,6 +155,7 @@ func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) {
} }
opts.Module = mod opts.Module = mod
opts.Parallelism = copts.Parallelism
opts.State = state.State() opts.State = state.State()
ctx := terraform.NewContext(opts) ctx := terraform.NewContext(opts)
return ctx, false, nil return ctx, false, nil
@ -430,4 +435,7 @@ type contextOpts struct {
// Set to true when running a destroy plan/apply. // Set to true when running a destroy plan/apply.
Destroy bool Destroy bool
// Number of concurrent operations allowed
Parallelism int
} }

View File

@ -27,6 +27,7 @@ func (c *PlanCommand) Run(args []string) int {
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
c.addModuleDepthFlag(cmdFlags, &moduleDepth) c.addModuleDepthFlag(cmdFlags, &moduleDepth)
cmdFlags.StringVar(&outPath, "out", "", "path") 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.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode") cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode")
@ -57,9 +58,10 @@ func (c *PlanCommand) Run(args []string) int {
c.Meta.extraHooks = []terraform.Hook{countHook} c.Meta.extraHooks = []terraform.Hook{countHook}
ctx, _, err := c.Context(contextOpts{ ctx, _, err := c.Context(contextOpts{
Destroy: destroy, Destroy: destroy,
Path: path, Path: path,
StatePath: c.Meta.statePath, StatePath: c.Meta.statePath,
Parallelism: c.Meta.parallelism,
}) })
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())

View File

@ -18,6 +18,7 @@ func (c *RefreshCommand) Run(args []string) int {
cmdFlags := c.Meta.flagSet("refresh") cmdFlags := c.Meta.flagSet("refresh")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") 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.stateOutPath, "state-out", "", "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
@ -78,8 +79,9 @@ func (c *RefreshCommand) Run(args []string) int {
// Build the context based on the arguments given // Build the context based on the arguments given
ctx, _, err := c.Context(contextOpts{ ctx, _, err := c.Context(contextOpts{
Path: configPath, Path: configPath,
StatePath: c.Meta.statePath, StatePath: c.Meta.statePath,
Parallelism: c.Meta.parallelism,
}) })
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())

View File

@ -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" {}
}