core: targeted operations
Add `-target=resource` flag to core operations, allowing users to target specific resources in their infrastructure. When `-target` is used, the operation will only apply to that resource and its dependencies. The calculated dependencies are different depending on whether we're running a normal operation or a `terraform destroy`. Generally, "dependencies" refers to ancestors: resources falling _before_ the target in the graph, because their changes are required to accurately act on the target. For destroys, "dependencies" are descendents: those resources which fall _after_ the target. These resources depend on our target, which is going to be destroyed, so they should also be destroyed.
This commit is contained in:
parent
766b4902d4
commit
97acccd3ed
|
@ -93,6 +93,7 @@ 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,
|
||||||
Path: configPath,
|
Path: configPath,
|
||||||
StatePath: c.Meta.statePath,
|
StatePath: c.Meta.statePath,
|
||||||
})
|
})
|
||||||
|
@ -140,12 +141,7 @@ func (c *ApplyCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var opts terraform.PlanOpts
|
if _, err := ctx.Plan(); err != nil {
|
||||||
if c.Destroy {
|
|
||||||
opts.Destroy = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := ctx.Plan(&opts); err != nil {
|
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
"Error creating plan: %s", err))
|
"Error creating plan: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
@ -319,6 +315,10 @@ Options:
|
||||||
"-state". This can be used to preserve the old
|
"-state". This can be used to preserve the old
|
||||||
state.
|
state.
|
||||||
|
|
||||||
|
-target=resource Resource to target. Operation will be limited to this
|
||||||
|
resource and its dependencies. This flag can be used
|
||||||
|
multiple times.
|
||||||
|
|
||||||
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
||||||
flag can be set multiple times.
|
flag can be set multiple times.
|
||||||
|
|
||||||
|
@ -357,6 +357,10 @@ Options:
|
||||||
"-state". This can be used to preserve the old
|
"-state". This can be used to preserve the old
|
||||||
state.
|
state.
|
||||||
|
|
||||||
|
-target=resource Resource to target. Operation will be limited to this
|
||||||
|
resource and its dependencies. This flag can be used
|
||||||
|
multiple times.
|
||||||
|
|
||||||
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
||||||
flag can be set multiple times.
|
flag can be set multiple times.
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,96 @@ func TestApply_destroyPlan(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApply_destroyTargeted(t *testing.T) {
|
||||||
|
originalState := &terraform.State{
|
||||||
|
Modules: []*terraform.ModuleState{
|
||||||
|
&terraform.ModuleState{
|
||||||
|
Path: []string{"root"},
|
||||||
|
Resources: map[string]*terraform.ResourceState{
|
||||||
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
|
Type: "test_instance",
|
||||||
|
Primary: &terraform.InstanceState{
|
||||||
|
ID: "i-ab123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"test_load_balancer.foo": &terraform.ResourceState{
|
||||||
|
Type: "test_load_balancer",
|
||||||
|
Primary: &terraform.InstanceState{
|
||||||
|
ID: "lb-abc123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
statePath := testStateFile(t, originalState)
|
||||||
|
|
||||||
|
p := testProvider()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ApplyCommand{
|
||||||
|
Destroy: true,
|
||||||
|
Meta: Meta{
|
||||||
|
ContextOpts: testCtxConfig(p),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the apply command pointing to our existing state
|
||||||
|
args := []string{
|
||||||
|
"-force",
|
||||||
|
"-target", "test_instance.foo",
|
||||||
|
"-state", statePath,
|
||||||
|
testFixturePath("apply-destroy-targeted"),
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify a new state exists
|
||||||
|
if _, err := os.Stat(statePath); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(statePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
state, err := terraform.ReadState(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if state == nil {
|
||||||
|
t.Fatal("state should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
actualStr := strings.TrimSpace(state.String())
|
||||||
|
expectedStr := strings.TrimSpace(testApplyDestroyStr)
|
||||||
|
if actualStr != expectedStr {
|
||||||
|
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should have a backup file
|
||||||
|
f, err = os.Open(statePath + DefaultBackupExtention)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
backupState, err := terraform.ReadState(f)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actualStr = strings.TrimSpace(backupState.String())
|
||||||
|
expectedStr = strings.TrimSpace(originalState.String())
|
||||||
|
if actualStr != expectedStr {
|
||||||
|
t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const testApplyDestroyStr = `
|
const testApplyDestroyStr = `
|
||||||
<no state>
|
<no state>
|
||||||
`
|
`
|
||||||
|
|
|
@ -85,3 +85,17 @@ func loadKVFile(rawPath string) (map[string]string, error) {
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FlagStringSlice is a flag.Value implementation for parsing targets from the
|
||||||
|
// command line, e.g. -target=aws_instance.foo -target=aws_vpc.bar
|
||||||
|
|
||||||
|
type FlagStringSlice []string
|
||||||
|
|
||||||
|
func (v *FlagStringSlice) String() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
func (v *FlagStringSlice) Set(raw string) error {
|
||||||
|
*v = append(*v, raw)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ type Meta struct {
|
||||||
input bool
|
input bool
|
||||||
variables map[string]string
|
variables map[string]string
|
||||||
|
|
||||||
|
// Targets for this context (private)
|
||||||
|
targets []string
|
||||||
|
|
||||||
color bool
|
color bool
|
||||||
oldUi cli.Ui
|
oldUi cli.Ui
|
||||||
|
|
||||||
|
@ -126,6 +129,9 @@ func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) {
|
||||||
m.statePath = copts.StatePath
|
m.statePath = copts.StatePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tell the context if we're in a destroy plan / apply
|
||||||
|
opts.Destroy = copts.Destroy
|
||||||
|
|
||||||
// Store the loaded state
|
// Store the loaded state
|
||||||
state, err := m.State()
|
state, err := m.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -267,6 +273,7 @@ func (m *Meta) contextOpts() *terraform.ContextOpts {
|
||||||
vs[k] = v
|
vs[k] = v
|
||||||
}
|
}
|
||||||
opts.Variables = vs
|
opts.Variables = vs
|
||||||
|
opts.Targets = m.targets
|
||||||
opts.UIInput = m.UIInput()
|
opts.UIInput = m.UIInput()
|
||||||
|
|
||||||
return &opts
|
return &opts
|
||||||
|
@ -278,6 +285,7 @@ func (m *Meta) flagSet(n string) *flag.FlagSet {
|
||||||
f.BoolVar(&m.input, "input", true, "input")
|
f.BoolVar(&m.input, "input", true, "input")
|
||||||
f.Var((*FlagKV)(&m.variables), "var", "variables")
|
f.Var((*FlagKV)(&m.variables), "var", "variables")
|
||||||
f.Var((*FlagKVFile)(&m.variables), "var-file", "variable file")
|
f.Var((*FlagKVFile)(&m.variables), "var-file", "variable file")
|
||||||
|
f.Var((*FlagStringSlice)(&m.targets), "target", "resource to target")
|
||||||
|
|
||||||
if m.autoKey != "" {
|
if m.autoKey != "" {
|
||||||
f.Var((*FlagKVFile)(&m.autoVariables), m.autoKey, "variable file")
|
f.Var((*FlagKVFile)(&m.autoVariables), m.autoKey, "variable file")
|
||||||
|
@ -388,4 +396,7 @@ type contextOpts struct {
|
||||||
|
|
||||||
// GetMode is the module.GetMode to use when loading the module tree.
|
// GetMode is the module.GetMode to use when loading the module tree.
|
||||||
GetMode module.GetMode
|
GetMode module.GetMode
|
||||||
|
|
||||||
|
// Set to true when running a destroy plan/apply.
|
||||||
|
Destroy bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ func (c *PlanCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, _, err := c.Context(contextOpts{
|
ctx, _, err := c.Context(contextOpts{
|
||||||
|
Destroy: destroy,
|
||||||
Path: path,
|
Path: path,
|
||||||
StatePath: c.Meta.statePath,
|
StatePath: c.Meta.statePath,
|
||||||
})
|
})
|
||||||
|
@ -86,7 +87,7 @@ func (c *PlanCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plan, err := ctx.Plan(&terraform.PlanOpts{Destroy: destroy})
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Error running plan: %s", err))
|
c.Ui.Error(fmt.Sprintf("Error running plan: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
@ -168,6 +169,10 @@ Options:
|
||||||
up Terraform-managed resources. By default it will
|
up Terraform-managed resources. By default it will
|
||||||
use the state "terraform.tfstate" if it exists.
|
use the state "terraform.tfstate" if it exists.
|
||||||
|
|
||||||
|
-target=resource Resource to target. Operation will be limited to this
|
||||||
|
resource and its dependencies. This flag can be used
|
||||||
|
multiple times.
|
||||||
|
|
||||||
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
||||||
flag can be set multiple times.
|
flag can be set multiple times.
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,10 @@ Options:
|
||||||
-state-out=path Path to write updated state file. By default, the
|
-state-out=path Path to write updated state file. By default, the
|
||||||
"-state" path will be used.
|
"-state" path will be used.
|
||||||
|
|
||||||
|
-target=resource Resource to target. Operation will be limited to this
|
||||||
|
resource and its dependencies. This flag can be used
|
||||||
|
multiple times.
|
||||||
|
|
||||||
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
-var 'foo=bar' Set a variable in the Terraform configuration. This
|
||||||
flag can be set multiple times.
|
flag can be set multiple times.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
count = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "test_load_balancer" "foo" {
|
||||||
|
instances = ["${test_instance.foo.*.id}"]
|
||||||
|
}
|
93
dag/dag.go
93
dag/dag.go
|
@ -17,6 +17,40 @@ type AcyclicGraph struct {
|
||||||
// WalkFunc is the callback used for walking the graph.
|
// WalkFunc is the callback used for walking the graph.
|
||||||
type WalkFunc func(Vertex) error
|
type WalkFunc func(Vertex) error
|
||||||
|
|
||||||
|
// Returns a Set that includes every Vertex yielded by walking down from the
|
||||||
|
// provided starting Vertex v.
|
||||||
|
func (g *AcyclicGraph) Ancestors(v Vertex) (*Set, error) {
|
||||||
|
s := new(Set)
|
||||||
|
start := asVertexList(g.DownEdges(v))
|
||||||
|
memoFunc := func(v Vertex) error {
|
||||||
|
s.Add(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := g.depthFirstWalk(start, memoFunc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a Set that includes every Vertex yielded by walking up from the
|
||||||
|
// provided starting Vertex v.
|
||||||
|
func (g *AcyclicGraph) Descendents(v Vertex) (*Set, error) {
|
||||||
|
s := new(Set)
|
||||||
|
start := asVertexList(g.UpEdges(v))
|
||||||
|
memoFunc := func(v Vertex) error {
|
||||||
|
s.Add(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := g.reverseDepthFirstWalk(start, memoFunc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Root returns the root of the DAG, or an error.
|
// Root returns the root of the DAG, or an error.
|
||||||
//
|
//
|
||||||
// Complexity: O(V)
|
// Complexity: O(V)
|
||||||
|
@ -61,15 +95,11 @@ func (g *AcyclicGraph) TransitiveReduction() {
|
||||||
|
|
||||||
for _, u := range g.Vertices() {
|
for _, u := range g.Vertices() {
|
||||||
uTargets := g.DownEdges(u)
|
uTargets := g.DownEdges(u)
|
||||||
vs := make([]Vertex, uTargets.Len())
|
vs := asVertexList(g.DownEdges(u))
|
||||||
for i, vRaw := range uTargets.List() {
|
|
||||||
vs[i] = vRaw.(Vertex)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.depthFirstWalk(vs, func(v Vertex) error {
|
g.depthFirstWalk(vs, func(v Vertex) error {
|
||||||
shared := uTargets.Intersection(g.DownEdges(v))
|
shared := uTargets.Intersection(g.DownEdges(v))
|
||||||
for _, raw := range shared.List() {
|
for _, vPrime := range asVertexList(shared) {
|
||||||
vPrime := raw.(Vertex)
|
|
||||||
g.RemoveEdge(BasicEdge(u, vPrime))
|
g.RemoveEdge(BasicEdge(u, vPrime))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,12 +175,10 @@ func (g *AcyclicGraph) Walk(cb WalkFunc) error {
|
||||||
for _, v := range vertices {
|
for _, v := range vertices {
|
||||||
// Build our list of dependencies and the list of channels to
|
// Build our list of dependencies and the list of channels to
|
||||||
// wait on until we start executing for this vertex.
|
// wait on until we start executing for this vertex.
|
||||||
depsRaw := g.DownEdges(v).List()
|
deps := asVertexList(g.DownEdges(v))
|
||||||
deps := make([]Vertex, len(depsRaw))
|
|
||||||
depChs := make([]<-chan struct{}, len(deps))
|
depChs := make([]<-chan struct{}, len(deps))
|
||||||
for i, raw := range depsRaw {
|
for i, dep := range deps {
|
||||||
deps[i] = raw.(Vertex)
|
depChs[i] = vertMap[dep]
|
||||||
depChs[i] = vertMap[deps[i]]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get our channel so that we can close it when we're done
|
// Get our channel so that we can close it when we're done
|
||||||
|
@ -200,6 +228,16 @@ func (g *AcyclicGraph) Walk(cb WalkFunc) error {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simple convenience helper for converting a dag.Set to a []Vertex
|
||||||
|
func asVertexList(s *Set) []Vertex {
|
||||||
|
rawList := s.List()
|
||||||
|
vertexList := make([]Vertex, len(rawList))
|
||||||
|
for i, raw := range rawList {
|
||||||
|
vertexList[i] = raw.(Vertex)
|
||||||
|
}
|
||||||
|
return vertexList
|
||||||
|
}
|
||||||
|
|
||||||
// depthFirstWalk does a depth-first walk of the graph starting from
|
// depthFirstWalk does a depth-first walk of the graph starting from
|
||||||
// the vertices in start. This is not exported now but it would make sense
|
// the vertices in start. This is not exported now but it would make sense
|
||||||
// to export this publicly at some point.
|
// to export this publicly at some point.
|
||||||
|
@ -233,3 +271,36 @@ func (g *AcyclicGraph) depthFirstWalk(start []Vertex, cb WalkFunc) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reverseDepthFirstWalk does a depth-first walk _up_ the graph starting from
|
||||||
|
// the vertices in start.
|
||||||
|
func (g *AcyclicGraph) reverseDepthFirstWalk(start []Vertex, cb WalkFunc) error {
|
||||||
|
seen := make(map[Vertex]struct{})
|
||||||
|
frontier := make([]Vertex, len(start))
|
||||||
|
copy(frontier, start)
|
||||||
|
for len(frontier) > 0 {
|
||||||
|
// Pop the current vertex
|
||||||
|
n := len(frontier)
|
||||||
|
current := frontier[n-1]
|
||||||
|
frontier = frontier[:n-1]
|
||||||
|
|
||||||
|
// Check if we've seen this already and return...
|
||||||
|
if _, ok := seen[current]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[current] = struct{}{}
|
||||||
|
|
||||||
|
// Visit the current node
|
||||||
|
if err := cb(current); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit targets of this in reverse order.
|
||||||
|
targets := g.UpEdges(current).List()
|
||||||
|
for i := len(targets) - 1; i >= 0; i-- {
|
||||||
|
frontier = append(frontier, targets[i].(Vertex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -126,6 +126,68 @@ func TestAcyclicGraphValidate_cycleSelf(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAcyclicGraphAncestors(t *testing.T) {
|
||||||
|
var g AcyclicGraph
|
||||||
|
g.Add(1)
|
||||||
|
g.Add(2)
|
||||||
|
g.Add(3)
|
||||||
|
g.Add(4)
|
||||||
|
g.Add(5)
|
||||||
|
g.Connect(BasicEdge(0, 1))
|
||||||
|
g.Connect(BasicEdge(1, 2))
|
||||||
|
g.Connect(BasicEdge(2, 3))
|
||||||
|
g.Connect(BasicEdge(3, 4))
|
||||||
|
g.Connect(BasicEdge(4, 5))
|
||||||
|
|
||||||
|
actual, err := g.Ancestors(2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []Vertex{3, 4, 5}
|
||||||
|
|
||||||
|
if actual.Len() != len(expected) {
|
||||||
|
t.Fatalf("bad length! expected %#v to have len %d", actual, len(expected))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range expected {
|
||||||
|
if !actual.Include(e) {
|
||||||
|
t.Fatalf("expected: %#v to include: %#v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAcyclicGraphDescendents(t *testing.T) {
|
||||||
|
var g AcyclicGraph
|
||||||
|
g.Add(1)
|
||||||
|
g.Add(2)
|
||||||
|
g.Add(3)
|
||||||
|
g.Add(4)
|
||||||
|
g.Add(5)
|
||||||
|
g.Connect(BasicEdge(0, 1))
|
||||||
|
g.Connect(BasicEdge(1, 2))
|
||||||
|
g.Connect(BasicEdge(2, 3))
|
||||||
|
g.Connect(BasicEdge(3, 4))
|
||||||
|
g.Connect(BasicEdge(4, 5))
|
||||||
|
|
||||||
|
actual, err := g.Descendents(2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []Vertex{0, 1}
|
||||||
|
|
||||||
|
if actual.Len() != len(expected) {
|
||||||
|
t.Fatalf("bad length! expected %#v to have len %d", actual, len(expected))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range expected {
|
||||||
|
if !actual.Include(e) {
|
||||||
|
t.Fatalf("expected: %#v to include: %#v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAcyclicGraphWalk(t *testing.T) {
|
func TestAcyclicGraphWalk(t *testing.T) {
|
||||||
var g AcyclicGraph
|
var g AcyclicGraph
|
||||||
g.Add(1)
|
g.Add(1)
|
||||||
|
|
|
@ -190,6 +190,7 @@ func testStep(
|
||||||
// Build the context
|
// Build the context
|
||||||
opts.Module = mod
|
opts.Module = mod
|
||||||
opts.State = state
|
opts.State = state
|
||||||
|
opts.Destroy = step.Destroy
|
||||||
ctx := terraform.NewContext(&opts)
|
ctx := terraform.NewContext(&opts)
|
||||||
if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
|
if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
|
||||||
estrs := make([]string, len(es))
|
estrs := make([]string, len(es))
|
||||||
|
@ -209,7 +210,7 @@ func testStep(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plan!
|
// Plan!
|
||||||
if p, err := ctx.Plan(&terraform.PlanOpts{Destroy: step.Destroy}); err != nil {
|
if p, err := ctx.Plan(); err != nil {
|
||||||
return state, fmt.Errorf(
|
return state, fmt.Errorf(
|
||||||
"Error planning: %s", err)
|
"Error planning: %s", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -33,6 +33,7 @@ const (
|
||||||
// ContextOpts are the user-configurable options to create a context with
|
// ContextOpts are the user-configurable options to create a context with
|
||||||
// NewContext.
|
// NewContext.
|
||||||
type ContextOpts struct {
|
type ContextOpts struct {
|
||||||
|
Destroy bool
|
||||||
Diff *Diff
|
Diff *Diff
|
||||||
Hooks []Hook
|
Hooks []Hook
|
||||||
Module *module.Tree
|
Module *module.Tree
|
||||||
|
@ -40,6 +41,7 @@ type ContextOpts struct {
|
||||||
State *State
|
State *State
|
||||||
Providers map[string]ResourceProviderFactory
|
Providers map[string]ResourceProviderFactory
|
||||||
Provisioners map[string]ResourceProvisionerFactory
|
Provisioners map[string]ResourceProvisionerFactory
|
||||||
|
Targets []string
|
||||||
Variables map[string]string
|
Variables map[string]string
|
||||||
|
|
||||||
UIInput UIInput
|
UIInput UIInput
|
||||||
|
@ -49,6 +51,7 @@ type ContextOpts struct {
|
||||||
// perform operations on infrastructure. This structure is built using
|
// perform operations on infrastructure. This structure is built using
|
||||||
// NewContext. See the documentation for that.
|
// NewContext. See the documentation for that.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
destroy bool
|
||||||
diff *Diff
|
diff *Diff
|
||||||
diffLock sync.RWMutex
|
diffLock sync.RWMutex
|
||||||
hooks []Hook
|
hooks []Hook
|
||||||
|
@ -58,6 +61,7 @@ type Context struct {
|
||||||
sh *stopHook
|
sh *stopHook
|
||||||
state *State
|
state *State
|
||||||
stateLock sync.RWMutex
|
stateLock sync.RWMutex
|
||||||
|
targets []string
|
||||||
uiInput UIInput
|
uiInput UIInput
|
||||||
variables map[string]string
|
variables map[string]string
|
||||||
|
|
||||||
|
@ -95,12 +99,14 @@ func NewContext(opts *ContextOpts) *Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Context{
|
return &Context{
|
||||||
|
destroy: opts.Destroy,
|
||||||
diff: opts.Diff,
|
diff: opts.Diff,
|
||||||
hooks: hooks,
|
hooks: hooks,
|
||||||
module: opts.Module,
|
module: opts.Module,
|
||||||
providers: opts.Providers,
|
providers: opts.Providers,
|
||||||
provisioners: opts.Provisioners,
|
provisioners: opts.Provisioners,
|
||||||
state: state,
|
state: state,
|
||||||
|
targets: opts.Targets,
|
||||||
uiInput: opts.UIInput,
|
uiInput: opts.UIInput,
|
||||||
variables: opts.Variables,
|
variables: opts.Variables,
|
||||||
|
|
||||||
|
@ -135,6 +141,8 @@ func (c *Context) GraphBuilder() GraphBuilder {
|
||||||
Providers: providers,
|
Providers: providers,
|
||||||
Provisioners: provisioners,
|
Provisioners: provisioners,
|
||||||
State: c.state,
|
State: c.state,
|
||||||
|
Targets: c.targets,
|
||||||
|
Destroy: c.destroy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +261,7 @@ func (c *Context) Apply() (*State, error) {
|
||||||
//
|
//
|
||||||
// Plan also updates the diff of this context to be the diff generated
|
// Plan also updates the diff of this context to be the diff generated
|
||||||
// by the plan, so Apply can be called after.
|
// by the plan, so Apply can be called after.
|
||||||
func (c *Context) Plan(opts *PlanOpts) (*Plan, error) {
|
func (c *Context) Plan() (*Plan, error) {
|
||||||
v := c.acquireRun()
|
v := c.acquireRun()
|
||||||
defer c.releaseRun(v)
|
defer c.releaseRun(v)
|
||||||
|
|
||||||
|
@ -264,7 +272,7 @@ func (c *Context) Plan(opts *PlanOpts) (*Plan, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var operation walkOperation
|
var operation walkOperation
|
||||||
if opts != nil && opts.Destroy {
|
if c.destroy {
|
||||||
operation = walkPlanDestroy
|
operation = walkPlanDestroy
|
||||||
} else {
|
} else {
|
||||||
// Set our state to be something temporary. We do this so that
|
// Set our state to be something temporary. We do this so that
|
||||||
|
|
|
@ -24,7 +24,7 @@ func TestContext2Plan(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func TestContext2Plan_emptyDiff(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ func TestContext2Plan_minimal(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ func TestContext2Plan_modules(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ func TestContext2Plan_moduleInput(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ func TestContext2Plan_moduleInputComputed(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ func TestContext2Plan_moduleInputFromVar(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ func TestContext2Plan_moduleMultiVar(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ func TestContext2Plan_moduleOrphans(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -282,7 +282,7 @@ func TestContext2Plan_moduleProviderInherit(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := ctx.Plan(nil)
|
_, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -332,7 +332,7 @@ func TestContext2Plan_moduleProviderDefaults(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := ctx.Plan(nil)
|
_, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -385,7 +385,7 @@ func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := ctx.Plan(nil)
|
_, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -410,7 +410,7 @@ func TestContext2Plan_moduleVar(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -433,7 +433,7 @@ func TestContext2Plan_moduleVarComputed(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -471,7 +471,7 @@ func TestContext2Plan_nil(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -491,7 +491,7 @@ func TestContext2Plan_computed(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -514,7 +514,7 @@ func TestContext2Plan_computedList(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -537,7 +537,7 @@ func TestContext2Plan_count(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -564,7 +564,7 @@ func TestContext2Plan_countComputed(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := ctx.Plan(nil)
|
_, err := ctx.Plan()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("should error")
|
t.Fatal("should error")
|
||||||
}
|
}
|
||||||
|
@ -581,7 +581,7 @@ func TestContext2Plan_countIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -604,7 +604,7 @@ func TestContext2Plan_countIndexZero(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -630,7 +630,7 @@ func TestContext2Plan_countVar(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -653,7 +653,7 @@ func TestContext2Plan_countZero(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -676,7 +676,7 @@ func TestContext2Plan_countOneIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -731,7 +731,7 @@ func TestContext2Plan_countDecreaseToOne(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -774,7 +774,7 @@ func TestContext2Plan_countIncreaseFromNotSet(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -817,7 +817,7 @@ func TestContext2Plan_countIncreaseFromOne(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -875,7 +875,7 @@ func TestContext2Plan_countIncreaseFromOneCorrupted(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -918,9 +918,10 @@ func TestContext2Plan_destroy(t *testing.T) {
|
||||||
"aws": testProviderFuncFixed(p),
|
"aws": testProviderFuncFixed(p),
|
||||||
},
|
},
|
||||||
State: s,
|
State: s,
|
||||||
|
Destroy: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(&PlanOpts{Destroy: true})
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -972,9 +973,10 @@ func TestContext2Plan_moduleDestroy(t *testing.T) {
|
||||||
"aws": testProviderFuncFixed(p),
|
"aws": testProviderFuncFixed(p),
|
||||||
},
|
},
|
||||||
State: s,
|
State: s,
|
||||||
|
Destroy: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(&PlanOpts{Destroy: true})
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1021,9 +1023,10 @@ func TestContext2Plan_moduleDestroyMultivar(t *testing.T) {
|
||||||
"aws": testProviderFuncFixed(p),
|
"aws": testProviderFuncFixed(p),
|
||||||
},
|
},
|
||||||
State: s,
|
State: s,
|
||||||
|
Destroy: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(&PlanOpts{Destroy: true})
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1051,7 +1054,7 @@ func TestContext2Plan_pathVar(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1118,7 +1121,7 @@ func TestContext2Plan_diffVar(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1143,7 +1146,7 @@ func TestContext2Plan_hook(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := ctx.Plan(nil)
|
_, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1183,7 +1186,7 @@ func TestContext2Plan_orphan(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1221,7 +1224,7 @@ func TestContext2Plan_state(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1273,7 +1276,7 @@ func TestContext2Plan_taint(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1324,7 +1327,7 @@ func TestContext2Plan_multiple_taint(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1357,7 +1360,7 @@ func TestContext2Plan_provider(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1377,7 +1380,7 @@ func TestContext2Plan_varMultiCountOne(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
plan, err := ctx.Plan(nil)
|
plan, err := ctx.Plan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1399,7 +1402,7 @@ func TestContext2Plan_varListErr(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := ctx.Plan(nil)
|
_, err := ctx.Plan()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("should error")
|
t.Fatal("should error")
|
||||||
}
|
}
|
||||||
|
@ -2468,7 +2471,7 @@ func TestContext2Input(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2513,7 +2516,7 @@ func TestContext2Input_provider(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2590,7 +2593,7 @@ func TestContext2Input_providerId(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2638,7 +2641,7 @@ func TestContext2Input_providerOnly(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2693,7 +2696,7 @@ func TestContext2Input_providerVars(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2741,7 +2744,7 @@ func TestContext2Input_varOnly(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2787,7 +2790,7 @@ func TestContext2Input_varOnlyUnset(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2815,7 +2818,7 @@ func TestContext2Apply(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2848,7 +2851,7 @@ func TestContext2Apply_emptyModule(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2896,7 +2899,7 @@ func TestContext2Apply_createBeforeDestroy(t *testing.T) {
|
||||||
State: state,
|
State: state,
|
||||||
})
|
})
|
||||||
|
|
||||||
if p, err := ctx.Plan(nil); err != nil {
|
if p, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
} else {
|
} else {
|
||||||
t.Logf(p.String())
|
t.Logf(p.String())
|
||||||
|
@ -2950,7 +2953,7 @@ func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) {
|
||||||
State: state,
|
State: state,
|
||||||
})
|
})
|
||||||
|
|
||||||
if p, err := ctx.Plan(nil); err != nil {
|
if p, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
} else {
|
} else {
|
||||||
t.Logf(p.String())
|
t.Logf(p.String())
|
||||||
|
@ -2985,7 +2988,7 @@ func TestContext2Apply_minimal(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3013,7 +3016,7 @@ func TestContext2Apply_badDiff(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3071,7 +3074,7 @@ func TestContext2Apply_cancel(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3112,7 +3115,7 @@ func TestContext2Apply_compute(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3181,7 +3184,7 @@ func TestContext2Apply_countDecrease(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3241,7 +3244,7 @@ func TestContext2Apply_countDecreaseToOne(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3303,7 +3306,7 @@ func TestContext2Apply_countDecreaseToOneCorrupted(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
if p, err := ctx.Plan(nil); err != nil {
|
if p, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
} else {
|
} else {
|
||||||
testStringMatch(t, p, testTerraformApplyCountDecToOneCorruptedPlanStr)
|
testStringMatch(t, p, testTerraformApplyCountDecToOneCorruptedPlanStr)
|
||||||
|
@ -3354,7 +3357,7 @@ func TestContext2Apply_countTainted(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3382,7 +3385,7 @@ func TestContext2Apply_countVariable(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3410,7 +3413,7 @@ func TestContext2Apply_module(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3439,9 +3442,10 @@ func TestContext2Apply_moduleVarResourceCount(t *testing.T) {
|
||||||
Variables: map[string]string{
|
Variables: map[string]string{
|
||||||
"count": "2",
|
"count": "2",
|
||||||
},
|
},
|
||||||
|
Destroy: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3459,7 +3463,7 @@ func TestContext2Apply_moduleVarResourceCount(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3481,7 +3485,7 @@ func TestContext2Apply_moduleBool(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3515,7 +3519,7 @@ func TestContext2Apply_multiProvider(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3548,7 +3552,7 @@ func TestContext2Apply_nilDiff(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3588,7 +3592,7 @@ func TestContext2Apply_Provisioner_compute(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3633,7 +3637,7 @@ func TestContext2Apply_provisionerCreateFail(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3672,7 +3676,7 @@ func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3712,7 +3716,7 @@ func TestContext2Apply_provisionerFail(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3767,7 +3771,7 @@ func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) {
|
||||||
State: state,
|
State: state,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3816,7 +3820,7 @@ func TestContext2Apply_error_createBeforeDestroy(t *testing.T) {
|
||||||
}
|
}
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3874,7 +3878,7 @@ func TestContext2Apply_errorDestroy_createBeforeDestroy(t *testing.T) {
|
||||||
}
|
}
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3931,7 +3935,7 @@ func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3955,7 +3959,7 @@ aws_instance.web: (1 deposed)
|
||||||
State: state,
|
State: state,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3983,7 +3987,7 @@ aws_instance.web: (2 deposed)
|
||||||
}
|
}
|
||||||
|
|
||||||
createdInstanceId = "qux"
|
createdInstanceId = "qux"
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
state, err = ctx.Apply()
|
state, err = ctx.Apply()
|
||||||
|
@ -4005,7 +4009,7 @@ aws_instance.web: (1 deposed)
|
||||||
}
|
}
|
||||||
|
|
||||||
createdInstanceId = "quux"
|
createdInstanceId = "quux"
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
state, err = ctx.Apply()
|
state, err = ctx.Apply()
|
||||||
|
@ -4045,7 +4049,7 @@ func TestContext2Apply_provisionerResourceRef(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4091,7 +4095,7 @@ func TestContext2Apply_provisionerSelfRef(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4144,7 +4148,7 @@ func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4192,7 +4196,7 @@ func TestContext2Apply_Provisioner_Diff(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4229,7 +4233,7 @@ func TestContext2Apply_Provisioner_Diff(t *testing.T) {
|
||||||
State: state,
|
State: state,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4300,7 +4304,7 @@ func TestContext2Apply_outputDiffVars(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
if _, err := ctx.Apply(); err != nil {
|
if _, err := ctx.Apply(); err != nil {
|
||||||
|
@ -4363,7 +4367,7 @@ func TestContext2Apply_Provisioner_ConnInfo(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4399,22 +4403,32 @@ func TestContext2Apply_destroy(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// First plan and apply a create operation
|
// First plan and apply a create operation
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Apply(); err != nil {
|
state, err := ctx.Apply()
|
||||||
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, plan and apply a destroy operation
|
// Next, plan and apply a destroy operation
|
||||||
if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil {
|
h.Active = true
|
||||||
|
ctx = testContext2(t, &ContextOpts{
|
||||||
|
Destroy: true,
|
||||||
|
State: state,
|
||||||
|
Module: m,
|
||||||
|
Hooks: []Hook{h},
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Active = true
|
state, err = ctx.Apply()
|
||||||
|
|
||||||
state, err := ctx.Apply()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -4430,7 +4444,7 @@ func TestContext2Apply_destroy(t *testing.T) {
|
||||||
expected2 := []string{"aws_instance.bar", "aws_instance.foo"}
|
expected2 := []string{"aws_instance.bar", "aws_instance.foo"}
|
||||||
actual2 := h.IDs
|
actual2 := h.IDs
|
||||||
if !reflect.DeepEqual(actual2, expected2) {
|
if !reflect.DeepEqual(actual2, expected2) {
|
||||||
t.Fatalf("bad: %#v", actual2)
|
t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4449,22 +4463,33 @@ func TestContext2Apply_destroyOutputs(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// First plan and apply a create operation
|
// First plan and apply a create operation
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Apply(); err != nil {
|
state, err := ctx.Apply()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, plan and apply a destroy operation
|
// Next, plan and apply a destroy operation
|
||||||
if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil {
|
h.Active = true
|
||||||
|
ctx = testContext2(t, &ContextOpts{
|
||||||
|
Destroy: true,
|
||||||
|
State: state,
|
||||||
|
Module: m,
|
||||||
|
Hooks: []Hook{h},
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Active = true
|
state, err = ctx.Apply()
|
||||||
|
|
||||||
state, err := ctx.Apply()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -4520,7 +4545,7 @@ func TestContext2Apply_destroyOrphan(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4578,9 +4603,10 @@ func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) {
|
||||||
"shell": testProvisionerFuncFixed(pr),
|
"shell": testProvisionerFuncFixed(pr),
|
||||||
},
|
},
|
||||||
State: s,
|
State: s,
|
||||||
|
Destroy: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4638,7 +4664,7 @@ func TestContext2Apply_error(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4705,7 +4731,7 @@ func TestContext2Apply_errorPartial(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4740,7 +4766,7 @@ func TestContext2Apply_hook(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4788,7 +4814,7 @@ func TestContext2Apply_idAttr(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4822,7 +4848,7 @@ func TestContext2Apply_output(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4850,7 +4876,7 @@ func TestContext2Apply_outputInvalid(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := ctx.Plan(nil)
|
_, err := ctx.Plan()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -4871,7 +4897,7 @@ func TestContext2Apply_outputList(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4899,7 +4925,7 @@ func TestContext2Apply_outputMulti(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4927,7 +4953,7 @@ func TestContext2Apply_outputMultiIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4992,7 +5018,7 @@ func TestContext2Apply_taint(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5057,7 +5083,7 @@ func TestContext2Apply_taintDep(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
if p, err := ctx.Plan(nil); err != nil {
|
if p, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("plan: %s", p)
|
t.Logf("plan: %s", p)
|
||||||
|
@ -5120,7 +5146,7 @@ func TestContext2Apply_taintDepRequiresNew(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
if p, err := ctx.Plan(nil); err != nil {
|
if p, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("plan: %s", p)
|
t.Logf("plan: %s", p)
|
||||||
|
@ -5150,7 +5176,7 @@ func TestContext2Apply_unknownAttribute(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5190,7 +5216,7 @@ func TestContext2Apply_vars(t *testing.T) {
|
||||||
t.Fatalf("bad: %s", e)
|
t.Fatalf("bad: %s", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5248,7 +5274,7 @@ func TestContext2Apply_createBefore_depends(t *testing.T) {
|
||||||
State: state,
|
State: state,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5357,7 +5383,7 @@ func TestContext2Apply_singleDestroy(t *testing.T) {
|
||||||
State: state,
|
State: state,
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
if _, err := ctx.Plan(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,13 @@ type BuiltinGraphBuilder struct {
|
||||||
|
|
||||||
// Provisioners is the list of provisioners supported.
|
// Provisioners is the list of provisioners supported.
|
||||||
Provisioners []string
|
Provisioners []string
|
||||||
|
|
||||||
|
// Targets is the user-specified list of resources to target.
|
||||||
|
Targets []string
|
||||||
|
|
||||||
|
// Destroy is set to true when we're in a `terraform destroy` or a
|
||||||
|
// `terraform plan -destroy`
|
||||||
|
Destroy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds the graph according to the steps returned by Steps.
|
// Build builds the graph according to the steps returned by Steps.
|
||||||
|
@ -104,6 +111,10 @@ func (b *BuiltinGraphBuilder) Steps() []GraphTransformer {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Optionally reduces the graph to a user-specified list of targets and
|
||||||
|
// their dependencies.
|
||||||
|
&TargetsTransformer{Targets: b.Targets, Destroy: b.Destroy},
|
||||||
|
|
||||||
// Create the destruction nodes
|
// Create the destruction nodes
|
||||||
&DestroyTransformer{},
|
&DestroyTransformer{},
|
||||||
&CreateBeforeDestroyTransformer{},
|
&CreateBeforeDestroyTransformer{},
|
||||||
|
|
|
@ -18,15 +18,6 @@ func init() {
|
||||||
gob.Register(make(map[string]string))
|
gob.Register(make(map[string]string))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlanOpts are the options used to generate an execution plan for
|
|
||||||
// Terraform.
|
|
||||||
type PlanOpts struct {
|
|
||||||
// If set to true, then the generated plan will destroy all resources
|
|
||||||
// that are created. Otherwise, it will move towards the desired state
|
|
||||||
// specified in the configuration.
|
|
||||||
Destroy bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plan represents a single Terraform execution plan, which contains
|
// Plan represents a single Terraform execution plan, which contains
|
||||||
// all the information necessary to make an infrastructure change.
|
// all the information necessary to make an infrastructure change.
|
||||||
type Plan struct {
|
type Plan struct {
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
resource "aws_vpc" "me" {}
|
||||||
|
|
||||||
|
resource "aws_subnet" "me" {
|
||||||
|
vpc_id = "${aws_vpc.me.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "me" {
|
||||||
|
subnet_id = "${aws_subnet.me.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc" "notme" {}
|
||||||
|
resource "aws_subnet" "notme" {}
|
||||||
|
resource "aws_instance" "notme" {}
|
||||||
|
resource "aws_instance" "notmeeither" {
|
||||||
|
name = "${aws_instance.me.id}"
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
resource "aws_vpc" "notme" {}
|
||||||
|
|
||||||
|
resource "aws_subnet" "notme" {
|
||||||
|
vpc_id = "${aws_vpc.notme.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "me" {
|
||||||
|
subnet_id = "${aws_subnet.notme.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "notme" {}
|
||||||
|
resource "aws_instance" "metoo" {
|
||||||
|
name = "${aws_instance.me.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elb" "me" {
|
||||||
|
instances = "${aws_instance.me.*.id}"
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import "github.com/hashicorp/terraform/dag"
|
||||||
|
|
||||||
|
// TargetsTransformer is a GraphTransformer that, when the user specifies a
|
||||||
|
// list of resources to target, limits the graph to only those resources and
|
||||||
|
// their dependencies.
|
||||||
|
type TargetsTransformer struct {
|
||||||
|
// List of targeted resource names specified by the user
|
||||||
|
Targets []string
|
||||||
|
|
||||||
|
// Set to true when we're in a `terraform destroy` or a
|
||||||
|
// `terraform plan -destroy`
|
||||||
|
Destroy bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TargetsTransformer) Transform(g *Graph) error {
|
||||||
|
if len(t.Targets) > 0 {
|
||||||
|
targetedNodes, err := t.selectTargetedNodes(g)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range g.Vertices() {
|
||||||
|
if !targetedNodes.Include(v) {
|
||||||
|
g.Remove(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TargetsTransformer) selectTargetedNodes(g *Graph) (*dag.Set, error) {
|
||||||
|
targetedNodes := new(dag.Set)
|
||||||
|
for _, v := range g.Vertices() {
|
||||||
|
// Keep all providers; they'll be pruned later if necessary
|
||||||
|
if r, ok := v.(GraphNodeProvider); ok {
|
||||||
|
targetedNodes.Add(r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the remaining filter, we only care about Resources and their deps
|
||||||
|
r, ok := v.(*GraphNodeConfigResource)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.resourceIsTarget(r) {
|
||||||
|
targetedNodes.Add(r)
|
||||||
|
|
||||||
|
var deps *dag.Set
|
||||||
|
var err error
|
||||||
|
if t.Destroy {
|
||||||
|
deps, err = g.Descendents(r)
|
||||||
|
} else {
|
||||||
|
deps, err = g.Ancestors(r)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range deps.List() {
|
||||||
|
targetedNodes.Add(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return targetedNodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TargetsTransformer) resourceIsTarget(r *GraphNodeConfigResource) bool {
|
||||||
|
for _, target := range t.Targets {
|
||||||
|
if target == r.Name() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTargetsTransformer(t *testing.T) {
|
||||||
|
mod := testModule(t, "transform-targets-basic")
|
||||||
|
|
||||||
|
g := Graph{Path: RootModulePath}
|
||||||
|
{
|
||||||
|
tf := &ConfigTransformer{Module: mod}
|
||||||
|
if err := tf.Transform(&g); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
transform := &TargetsTransformer{Targets: []string{"aws_instance.me"}}
|
||||||
|
if err := transform.Transform(&g); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := strings.TrimSpace(g.String())
|
||||||
|
expected := strings.TrimSpace(`
|
||||||
|
aws_instance.me
|
||||||
|
aws_subnet.me
|
||||||
|
aws_subnet.me
|
||||||
|
aws_vpc.me
|
||||||
|
aws_vpc.me
|
||||||
|
`)
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("bad:\n\nexpected:\n%s\n\ngot:\n%s\n", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTargetsTransformer_destroy(t *testing.T) {
|
||||||
|
mod := testModule(t, "transform-targets-destroy")
|
||||||
|
|
||||||
|
g := Graph{Path: RootModulePath}
|
||||||
|
{
|
||||||
|
tf := &ConfigTransformer{Module: mod}
|
||||||
|
if err := tf.Transform(&g); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
transform := &TargetsTransformer{
|
||||||
|
Targets: []string{"aws_instance.me"},
|
||||||
|
Destroy: true,
|
||||||
|
}
|
||||||
|
if err := transform.Transform(&g); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := strings.TrimSpace(g.String())
|
||||||
|
expected := strings.TrimSpace(`
|
||||||
|
aws_elb.me
|
||||||
|
aws_instance.me
|
||||||
|
aws_instance.me
|
||||||
|
aws_instance.metoo
|
||||||
|
aws_instance.me
|
||||||
|
`)
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("bad:\n\nexpected:\n%s\n\ngot:\n%s\n", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue