github.com/rmenn/terraform@v0.3.8-0.20150225065417-fc84b3a78802/helper/schema/resource.go (about) 1 package schema 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/hashicorp/terraform/terraform" 8 ) 9 10 // Resource represents a thing in Terraform that has a set of configurable 11 // attributes and a lifecycle (create, read, update, delete). 12 // 13 // The Resource schema is an abstraction that allows provider writers to 14 // worry only about CRUD operations while off-loading validation, diff 15 // generation, etc. to this higher level library. 16 type Resource struct { 17 // Schema is the schema for the configuration of this resource. 18 // 19 // The keys of this map are the configuration keys, and the values 20 // describe the schema of the configuration value. 21 // 22 // The schema is used to represent both configurable data as well 23 // as data that might be computed in the process of creating this 24 // resource. 25 Schema map[string]*Schema 26 27 // The functions below are the CRUD operations for this resource. 28 // 29 // The only optional operation is Update. If Update is not implemented, 30 // then updates will not be supported for this resource. 31 // 32 // The ResourceData parameter in the functions below are used to 33 // query configuration and changes for the resource as well as to set 34 // the ID, computed data, etc. 35 // 36 // The interface{} parameter is the result of the ConfigureFunc in 37 // the provider for this resource. If the provider does not define 38 // a ConfigureFunc, this will be nil. This parameter should be used 39 // to store API clients, configuration structures, etc. 40 // 41 // If any errors occur during each of the operation, an error should be 42 // returned. If a resource was partially updated, be careful to enable 43 // partial state mode for ResourceData and use it accordingly. 44 // 45 // Exists is a function that is called to check if a resource still 46 // exists. If this returns false, then this will affect the diff 47 // accordingly. If this function isn't set, it will not be called. It 48 // is highly recommended to set it. The *ResourceData passed to Exists 49 // should _not_ be modified. 50 Create CreateFunc 51 Read ReadFunc 52 Update UpdateFunc 53 Delete DeleteFunc 54 Exists ExistsFunc 55 } 56 57 // See Resource documentation. 58 type CreateFunc func(*ResourceData, interface{}) error 59 60 // See Resource documentation. 61 type ReadFunc func(*ResourceData, interface{}) error 62 63 // See Resource documentation. 64 type UpdateFunc func(*ResourceData, interface{}) error 65 66 // See Resource documentation. 67 type DeleteFunc func(*ResourceData, interface{}) error 68 69 // See Resource documentation. 70 type ExistsFunc func(*ResourceData, interface{}) (bool, error) 71 72 // Apply creates, updates, and/or deletes a resource. 73 func (r *Resource) Apply( 74 s *terraform.InstanceState, 75 d *terraform.InstanceDiff, 76 meta interface{}) (*terraform.InstanceState, error) { 77 data, err := schemaMap(r.Schema).Data(s, d) 78 if err != nil { 79 return s, err 80 } 81 82 if s == nil { 83 // The Terraform API dictates that this should never happen, but 84 // it doesn't hurt to be safe in this case. 85 s = new(terraform.InstanceState) 86 } 87 88 if d.Destroy || d.RequiresNew() { 89 if s.ID != "" { 90 // Destroy the resource since it is created 91 if err := r.Delete(data, meta); err != nil { 92 return data.State(), err 93 } 94 95 // Make sure the ID is gone. 96 data.SetId("") 97 } 98 99 // If we're only destroying, and not creating, then return 100 // now since we're done! 101 if !d.RequiresNew() { 102 return nil, nil 103 } 104 105 // Reset the data to be stateless since we just destroyed 106 data, err = schemaMap(r.Schema).Data(nil, d) 107 if err != nil { 108 return nil, err 109 } 110 } 111 112 err = nil 113 if data.Id() == "" { 114 // We're creating, it is a new resource. 115 err = r.Create(data, meta) 116 } else { 117 if r.Update == nil { 118 return s, fmt.Errorf("doesn't support update") 119 } 120 121 err = r.Update(data, meta) 122 } 123 124 return data.State(), err 125 } 126 127 // Diff returns a diff of this resource and is API compatible with the 128 // ResourceProvider interface. 129 func (r *Resource) Diff( 130 s *terraform.InstanceState, 131 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 132 return schemaMap(r.Schema).Diff(s, c) 133 } 134 135 // Validate validates the resource configuration against the schema. 136 func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { 137 return schemaMap(r.Schema).Validate(c) 138 } 139 140 // Refresh refreshes the state of the resource. 141 func (r *Resource) Refresh( 142 s *terraform.InstanceState, 143 meta interface{}) (*terraform.InstanceState, error) { 144 if r.Exists != nil { 145 // Make a copy of data so that if it is modified it doesn't 146 // affect our Read later. 147 data, err := schemaMap(r.Schema).Data(s, nil) 148 if err != nil { 149 return s, err 150 } 151 152 exists, err := r.Exists(data, meta) 153 if err != nil { 154 return s, err 155 } 156 if !exists { 157 return nil, nil 158 } 159 } 160 161 data, err := schemaMap(r.Schema).Data(s, nil) 162 if err != nil { 163 return s, err 164 } 165 166 err = r.Read(data, meta) 167 state := data.State() 168 if state != nil && state.ID == "" { 169 state = nil 170 } 171 172 return state, err 173 } 174 175 // InternalValidate should be called to validate the structure 176 // of the resource. 177 // 178 // This should be called in a unit test for any resource to verify 179 // before release that a resource is properly configured for use with 180 // this library. 181 // 182 // Provider.InternalValidate() will automatically call this for all of 183 // the resources it manages, so you don't need to call this manually if it 184 // is part of a Provider. 185 func (r *Resource) InternalValidate() error { 186 if r == nil { 187 return errors.New("resource is nil") 188 } 189 190 return schemaMap(r.Schema).InternalValidate() 191 }