Skip to content
Permalink
Browse files

helper/schema: initial work

  • Loading branch information
mitchellh committed Aug 13, 2014
1 parent 3e3be5e commit 01b6b5f48e02a1113d628c039bc607c67c27f9f2
Showing with 173 additions and 0 deletions.
  1. +56 −0 helper/schema/resource.go
  2. +4 −0 helper/schema/resource_data.go
  3. +77 −0 helper/schema/resource_test.go
  4. +36 −0 helper/schema/schema.go
@@ -0,0 +1,56 @@
package schema

import (
"errors"
"fmt"
)

// The functions below are the CRUD function types for a Resource.
type CreateFunc func(*ResourceData) error
type ReadFunc func(*ResourceData) error
type UpdateFunc func(*ResourceData) error
type DeleteFunc func(*ResourceData) error

// Resource represents a thing in Terraform that has a set of configurable
// attributes and generally also has a lifecycle (create, read, update,
// delete).
//
// 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.
type Resource struct {
Schema map[string]*Schema

Create CreateFunc
Read ReadFunc
Update UpdateFunc
Delete DeleteFunc
}

// InternalValidate should be called to validate the structure
// of the resource.
//
// This should be called in a unit test for any resource to verify
// before release that a resource is properly configured for use with
// this library.
func (r *Resource) InternalValidate() error {
if r == nil {
return errors.New("resource is nil")
}

for k, v := range r.Schema {
if v.Type == TypeInvalid {
return fmt.Errorf("%s: Type must be specified", k)
}

if v.Optional && v.Required {
return fmt.Errorf("%s: Optional or Required must be set, not both", k)
}

if v.Required && v.Computed {
return fmt.Errorf("%s: Cannot be both Required and Computed", k)
}
}

return nil
}
@@ -0,0 +1,4 @@
package schema

// ResourceData is used to query and set the attributes of a resource.
type ResourceData struct{}
@@ -0,0 +1,77 @@
package schema

import (
"testing"
)

func TestResourceInternalValidate(t *testing.T) {
cases := []struct {
In *Resource
Err bool
}{
{
nil,
true,
},

// No optional and no required
{
&Resource{
Schema: map[string]*Schema{
"foo": &Schema{
Type: TypeInt,
Optional: true,
Required: true,
},
},
},
true,
},

// Missing Type
{
&Resource{
Schema: map[string]*Schema{
"foo": &Schema{
Required: true,
},
},
},
true,
},

// Required but computed
{
&Resource{
Schema: map[string]*Schema{
"foo": &Schema{
Type: TypeInt,
Required: true,
Computed: true,
},
},
},
true,
},

// Looks good
{
&Resource{
Schema: map[string]*Schema{
"foo": &Schema{
Type: TypeString,
Required: true,
},
},
},
false,
},
}

for i, tc := range cases {
err := tc.In.InternalValidate()
if (err != nil) != tc.Err {
t.Fatalf("%d: bad: %s", i, err)
}
}
}
@@ -0,0 +1,36 @@
package schema

// ValueType is an enum of the type that can be represented by a schema.
type ValueType int

const (
TypeInvalid ValueType = iota
TypeBoolean
TypeInt
TypeString
TypeList
)

// Schema is used to describe the structure of a value.
type Schema struct {
// Type is the type of the value and must be one of the ValueType values.
Type ValueType

// If one of these is set, then this item can come from the configuration.
// Both cannot be set. If Optional is set, the value is optional. If
// Required is set, the value is required.
Optional bool
Required bool

// The fields below relate to diffs: if Computed is true, then the
// result of this value is computed (unless specified by config).
// If ForceNew is true
Computed bool
ForceNew bool

// Elem must be either a *Schema or a *Resource only if the Type is
// TypeList, and represents what the element type is. If it is *Schema,
// the element type is just a simple value. If it is *Resource, the
// element type is a complex structure, potentially with its own lifecycle.
Elem interface{}
}

0 comments on commit 01b6b5f

Please sign in to comment.
You can’t perform that action at this time.