Merge pull request #766 from hashicorp/f-exists-api

helper/schema: Exists API
This commit is contained in:
Mitchell Hashimoto 2015-01-16 10:56:25 -08:00
commit 466a54cfe4
3 changed files with 99 additions and 0 deletions

View File

@ -41,10 +41,17 @@ type Resource struct {
// If any errors occur during each of the operation, an error should be // If any errors occur during each of the operation, an error should be
// returned. If a resource was partially updated, be careful to enable // returned. If a resource was partially updated, be careful to enable
// partial state mode for ResourceData and use it accordingly. // partial state mode for ResourceData and use it accordingly.
//
// Exists is a function that is called to check if a resource still
// exists. If this returns false, then this will affect the diff
// accordingly. If this function isn't set, it will not be called. It
// is highly recommended to set it. The *ResourceData passed to Exists
// should _not_ be modified.
Create CreateFunc Create CreateFunc
Read ReadFunc Read ReadFunc
Update UpdateFunc Update UpdateFunc
Delete DeleteFunc Delete DeleteFunc
Exists ExistsFunc
} }
// See Resource documentation. // See Resource documentation.
@ -59,6 +66,9 @@ type UpdateFunc func(*ResourceData, interface{}) error
// See Resource documentation. // See Resource documentation.
type DeleteFunc func(*ResourceData, interface{}) error type DeleteFunc func(*ResourceData, interface{}) error
// See Resource documentation.
type ExistsFunc func(*ResourceData, interface{}) (bool, error)
// Apply creates, updates, and/or deletes a resource. // Apply creates, updates, and/or deletes a resource.
func (r *Resource) Apply( func (r *Resource) Apply(
s *terraform.InstanceState, s *terraform.InstanceState,
@ -131,6 +141,23 @@ func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) {
func (r *Resource) Refresh( func (r *Resource) Refresh(
s *terraform.InstanceState, s *terraform.InstanceState,
meta interface{}) (*terraform.InstanceState, error) { meta interface{}) (*terraform.InstanceState, error) {
if r.Exists != nil {
// Make a copy of data so that if it is modified it doesn't
// affect our Read later.
data, err := schemaMap(r.Schema).Data(s, nil)
if err != nil {
return s, err
}
exists, err := r.Exists(data, meta)
if err != nil {
return s, err
}
if !exists {
return nil, nil
}
}
data, err := schemaMap(r.Schema).Data(s, nil) data, err := schemaMap(r.Schema).Data(s, nil)
if err != nil { if err != nil {
return s, err return s, err

View File

@ -410,3 +410,71 @@ func TestResourceRefresh_delete(t *testing.T) {
t.Fatalf("bad: %#v", actual) t.Fatalf("bad: %#v", actual)
} }
} }
func TestResourceRefresh_existsError(t *testing.T) {
r := &Resource{
Schema: map[string]*Schema{
"foo": &Schema{
Type: TypeInt,
Optional: true,
},
},
}
r.Exists = func(*ResourceData, interface{}) (bool, error) {
return false, fmt.Errorf("error")
}
r.Read = func(d *ResourceData, m interface{}) error {
panic("shouldn't be called")
}
s := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"foo": "12",
},
}
actual, err := r.Refresh(s, 42)
if err == nil {
t.Fatalf("should error")
}
if !reflect.DeepEqual(actual, s) {
t.Fatalf("bad: %#v", actual)
}
}
func TestResourceRefresh_noExists(t *testing.T) {
r := &Resource{
Schema: map[string]*Schema{
"foo": &Schema{
Type: TypeInt,
Optional: true,
},
},
}
r.Exists = func(*ResourceData, interface{}) (bool, error) {
return false, nil
}
r.Read = func(d *ResourceData, m interface{}) error {
panic("shouldn't be called")
}
s := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"foo": "12",
},
}
actual, err := r.Refresh(s, 42)
if err != nil {
t.Fatalf("err: %s", err)
}
if actual != nil {
t.Fatalf("should have no state")
}
}

View File

@ -158,6 +158,10 @@ The CRUD operations in more detail, along with their contracts:
* `Delete` - This is called to delete the resource. Terraform guarantees * `Delete` - This is called to delete the resource. Terraform guarantees
an existing ID will be set. an existing ID will be set.
* `Exists` - This is called to verify a resource still exists. It is
called prior to `Read`, and lowers the burden of `Read` to be able
to assume the resource exists.
## Schemas ## Schemas
Both providers and resources require a schema to be specified. The schema Both providers and resources require a schema to be specified. The schema