From 6a468dcd83ef3a638294c325fa57d2b52c8bded2 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 16 Apr 2016 17:27:00 -0700 Subject: [PATCH] helper/schema: Resource can be writable or not In the "schema" layer a Resource is just any "thing" that has a schema and supports some or all of the CRUD operations. Data sources introduce a new use of Resource to represent read-only resources, which require some different InternalValidate logic. --- helper/schema/provider.go | 10 ++------- helper/schema/resource.go | 15 +++++++++++-- helper/schema/resource_test.go | 40 +++++++++++++++++++++++++++++++--- helper/schema/schema.go | 2 +- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/helper/schema/provider.go b/helper/schema/provider.go index 7b3371c8ac..c9ec7194e2 100644 --- a/helper/schema/provider.go +++ b/helper/schema/provider.go @@ -75,21 +75,15 @@ func (p *Provider) InternalValidate() error { } for k, r := range p.ResourcesMap { - if err := r.InternalValidate(nil); err != nil { + if err := r.InternalValidate(nil, true); err != nil { return fmt.Errorf("resource %s: %s", k, err) } } for k, r := range p.DataSourcesMap { - if err := r.InternalValidate(nil); err != nil { + if err := r.InternalValidate(nil, false); err != nil { return fmt.Errorf("data source %s: %s", k, err) } - - if r.Create != nil || r.Update != nil || r.Delete != nil { - return fmt.Errorf( - "data source %s: must not have Create, Update or Delete", k, - ) - } } return nil diff --git a/helper/schema/resource.go b/helper/schema/resource.go index 9d2ad8007c..54be1b20cb 100644 --- a/helper/schema/resource.go +++ b/helper/schema/resource.go @@ -14,6 +14,10 @@ import ( // The Resource schema is an abstraction that allows provider writers to // worry only about CRUD operations while off-loading validation, diff // generation, etc. to this higher level library. +// +// In spite of the name, this struct is not used only for terraform resources, +// but also for data sources. In the case of data sources, the Create, +// Update and Delete functions must not be provided. type Resource struct { // Schema is the schema for the configuration of this resource. // @@ -260,13 +264,20 @@ func (r *Resource) Refresh( // Provider.InternalValidate() will automatically call this for all of // the resources it manages, so you don't need to call this manually if it // is part of a Provider. -func (r *Resource) InternalValidate(topSchemaMap schemaMap) error { +func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error { if r == nil { return errors.New("resource is nil") } + + if !writable { + if r.Create != nil || r.Update != nil || r.Delete != nil { + return fmt.Errorf("must not implement Create, Update or Delete") + } + } + tsm := topSchemaMap - if r.isTopLevel() { + if r.isTopLevel() && writable { // All non-Computed attributes must be ForceNew if Update is not defined if r.Update == nil { nonForceNewAttrs := make([]string, 0) diff --git a/helper/schema/resource_test.go b/helper/schema/resource_test.go index 6a646d4104..3597050695 100644 --- a/helper/schema/resource_test.go +++ b/helper/schema/resource_test.go @@ -395,12 +395,14 @@ func TestResourceApply_isNewResource(t *testing.T) { func TestResourceInternalValidate(t *testing.T) { cases := []struct { - In *Resource - Err bool + In *Resource + Writable bool + Err bool }{ { nil, true, + true, }, // No optional and no required @@ -415,6 +417,7 @@ func TestResourceInternalValidate(t *testing.T) { }, }, true, + true, }, // Update undefined for non-ForceNew field @@ -429,6 +432,7 @@ func TestResourceInternalValidate(t *testing.T) { }, }, true, + true, }, // Update defined for ForceNew field @@ -445,11 +449,41 @@ func TestResourceInternalValidate(t *testing.T) { }, }, true, + true, + }, + + // non-writable doesn't need Update, Create or Delete + { + &Resource{ + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + false, + false, + }, + + // non-writable *must not* have Create + { + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + false, + true, }, } for i, tc := range cases { - err := tc.In.InternalValidate(schemaMap{}) + err := tc.In.InternalValidate(schemaMap{}, tc.Writable) if err != nil != tc.Err { t.Fatalf("%d: bad: %s", i, err) } diff --git a/helper/schema/schema.go b/helper/schema/schema.go index acffd249e6..deed582c13 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -553,7 +553,7 @@ func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { switch t := v.Elem.(type) { case *Resource: - if err := t.InternalValidate(topSchemaMap); err != nil { + if err := t.InternalValidate(topSchemaMap, true); err != nil { return err } case *Schema: