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

helper/schema: Allow identification of a new resource in update func #4446

Merged
merged 1 commit into from
Feb 29, 2016
Merged
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
1 change: 1 addition & 0 deletions helper/schema/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func (r *Resource) Apply(
err = nil
if data.Id() == "" {
// We're creating, it is a new resource.
data.MarkNewResource()
err = r.Create(data, meta)
} else {
if r.Update == nil {
Expand Down
9 changes: 9 additions & 0 deletions helper/schema/resource_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type ResourceData struct {
partial bool
partialMap map[string]struct{}
once sync.Once
isNew bool
}

// getResult is the internal structure that is generated when a Get
Expand Down Expand Up @@ -171,6 +172,14 @@ func (d *ResourceData) SetPartial(k string) {
}
}

func (d *ResourceData) MarkNewResource() {
d.isNew = true
}

func (d *ResourceData) IsNewResource() bool {
return d.isNew
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason to not just have IsNewResource() check Id() == "" directly rather than keeping the boolean struct member?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is a good point. Making IsNewResource() just an "alias" for Id() == "" is much cleaner approach. I will update the PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

In fact it doesn't seem to be equivalent, tests are failing.

It's been over a week since I last had my head in this part of codebase, so I don't quite know why, but I'll find out and post an explanation here.

Copy link
Member Author

Choose a reason for hiding this comment

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

So this is because if we have func Create() { return Update() }

then the Create func already sets id for the resource which means that Id() == "" in Update() function will never be true.

Resources with ForceNew: true are also covered:
https://github.com/TimeIncOSS/terraform/blob/f-schema-new-resource/helper/schema/resource.go#L119-L127


Looking at the code also makes me wonder why do we explicitly call d.SetId("") in most Delete() functions in resources, if we're resetting it here anyway. It seems superfluous.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ahhh sure that makes sense. This implementation works then.


// Id returns the ID of the resource.
func (d *ResourceData) Id() string {
var result string
Expand Down
81 changes: 81 additions & 0 deletions helper/schema/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,87 @@ func TestResourceApply_updateNoCallback(t *testing.T) {
}
}

func TestResourceApply_isNewResource(t *testing.T) {
r := &Resource{
Schema: map[string]*Schema{
"foo": &Schema{
Type: TypeString,
Optional: true,
},
},
}

updateFunc := func(d *ResourceData, m interface{}) error {
d.Set("foo", "updated")
if d.IsNewResource() {
d.Set("foo", "new-resource")
}
return nil
}
r.Create = func(d *ResourceData, m interface{}) error {
d.SetId("foo")
d.Set("foo", "created")
return updateFunc(d, m)
}
r.Update = updateFunc

d := &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"foo": &terraform.ResourceAttrDiff{
New: "bla-blah",
},
},
}

// positive test
var s *terraform.InstanceState = nil

actual, err := r.Apply(s, d, nil)
if err != nil {
t.Fatalf("err: %s", err)
}

expected := &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"id": "foo",
"foo": "new-resource",
},
}

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("actual: %#v\nexpected: %#v",
actual, expected)
}

// negative test
s = &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"id": "foo",
"foo": "new-resource",
},
}

actual, err = r.Apply(s, d, nil)
if err != nil {
t.Fatalf("err: %s", err)
}

expected = &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"id": "foo",
"foo": "updated",
},
}

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("actual: %#v\nexpected: %#v",
actual, expected)
}
}

func TestResourceInternalValidate(t *testing.T) {
cases := []struct {
In *Resource
Expand Down