github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/legacy/helper/schema/provisioner.go (about) 1 package schema 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sync" 8 9 "github.com/hashicorp/go-multierror" 10 "github.com/hashicorp/terraform/internal/configs/configschema" 11 "github.com/hashicorp/terraform/internal/legacy/terraform" 12 ) 13 14 // Provisioner represents a resource provisioner in Terraform and properly 15 // implements all of the ResourceProvisioner API. 16 // 17 // This higher level structure makes it much easier to implement a new or 18 // custom provisioner for Terraform. 19 // 20 // The function callbacks for this structure are all passed a context object. 21 // This context object has a number of pre-defined values that can be accessed 22 // via the global functions defined in context.go. 23 type Provisioner struct { 24 // ConnSchema is the schema for the connection settings for this 25 // provisioner. 26 // 27 // The keys of this map are the configuration keys, and the value is 28 // the schema describing the value of the configuration. 29 // 30 // NOTE: The value of connection keys can only be strings for now. 31 ConnSchema map[string]*Schema 32 33 // Schema is the schema for the usage of this provisioner. 34 // 35 // The keys of this map are the configuration keys, and the value is 36 // the schema describing the value of the configuration. 37 Schema map[string]*Schema 38 39 // ApplyFunc is the function for executing the provisioner. This is required. 40 // It is given a context. See the Provisioner struct docs for more 41 // information. 42 ApplyFunc func(ctx context.Context) error 43 44 // ValidateFunc is a function for extended validation. This is optional 45 // and should be used when individual field validation is not enough. 46 ValidateFunc func(*terraform.ResourceConfig) ([]string, []error) 47 48 stopCtx context.Context 49 stopCtxCancel context.CancelFunc 50 stopOnce sync.Once 51 } 52 53 // Keys that can be used to access data in the context parameters for 54 // Provisioners. 55 var ( 56 connDataInvalid = contextKey("data invalid") 57 58 // This returns a *ResourceData for the connection information. 59 // Guaranteed to never be nil. 60 ProvConnDataKey = contextKey("provider conn data") 61 62 // This returns a *ResourceData for the config information. 63 // Guaranteed to never be nil. 64 ProvConfigDataKey = contextKey("provider config data") 65 66 // This returns a terraform.UIOutput. Guaranteed to never be nil. 67 ProvOutputKey = contextKey("provider output") 68 69 // This returns the raw InstanceState passed to Apply. Guaranteed to 70 // be set, but may be nil. 71 ProvRawStateKey = contextKey("provider raw state") 72 ) 73 74 // InternalValidate should be called to validate the structure 75 // of the provisioner. 76 // 77 // This should be called in a unit test to verify before release that this 78 // structure is properly configured for use. 79 func (p *Provisioner) InternalValidate() error { 80 if p == nil { 81 return errors.New("provisioner is nil") 82 } 83 84 var validationErrors error 85 { 86 sm := schemaMap(p.ConnSchema) 87 if err := sm.InternalValidate(sm); err != nil { 88 validationErrors = multierror.Append(validationErrors, err) 89 } 90 } 91 92 { 93 sm := schemaMap(p.Schema) 94 if err := sm.InternalValidate(sm); err != nil { 95 validationErrors = multierror.Append(validationErrors, err) 96 } 97 } 98 99 if p.ApplyFunc == nil { 100 validationErrors = multierror.Append(validationErrors, fmt.Errorf( 101 "ApplyFunc must not be nil")) 102 } 103 104 return validationErrors 105 } 106 107 // StopContext returns a context that checks whether a provisioner is stopped. 108 func (p *Provisioner) StopContext() context.Context { 109 p.stopOnce.Do(p.stopInit) 110 return p.stopCtx 111 } 112 113 func (p *Provisioner) stopInit() { 114 p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background()) 115 } 116 117 // Stop implementation of terraform.ResourceProvisioner interface. 118 func (p *Provisioner) Stop() error { 119 p.stopOnce.Do(p.stopInit) 120 p.stopCtxCancel() 121 return nil 122 } 123 124 // GetConfigSchema implementation of terraform.ResourceProvisioner interface. 125 func (p *Provisioner) GetConfigSchema() (*configschema.Block, error) { 126 return schemaMap(p.Schema).CoreConfigSchema(), nil 127 } 128 129 // Apply implementation of terraform.ResourceProvisioner interface. 130 func (p *Provisioner) Apply( 131 o terraform.UIOutput, 132 s *terraform.InstanceState, 133 c *terraform.ResourceConfig) error { 134 var connData, configData *ResourceData 135 136 { 137 // We first need to turn the connection information into a 138 // terraform.ResourceConfig so that we can use that type to more 139 // easily build a ResourceData structure. We do this by simply treating 140 // the conn info as configuration input. 141 raw := make(map[string]interface{}) 142 if s != nil { 143 for k, v := range s.Ephemeral.ConnInfo { 144 raw[k] = v 145 } 146 } 147 148 c := terraform.NewResourceConfigRaw(raw) 149 sm := schemaMap(p.ConnSchema) 150 diff, err := sm.Diff(nil, c, nil, nil, true) 151 if err != nil { 152 return err 153 } 154 connData, err = sm.Data(nil, diff) 155 if err != nil { 156 return err 157 } 158 } 159 160 { 161 // Build the configuration data. Doing this requires making a "diff" 162 // even though that's never used. We use that just to get the correct types. 163 configMap := schemaMap(p.Schema) 164 diff, err := configMap.Diff(nil, c, nil, nil, true) 165 if err != nil { 166 return err 167 } 168 configData, err = configMap.Data(nil, diff) 169 if err != nil { 170 return err 171 } 172 } 173 174 // Build the context and call the function 175 ctx := p.StopContext() 176 ctx = context.WithValue(ctx, ProvConnDataKey, connData) 177 ctx = context.WithValue(ctx, ProvConfigDataKey, configData) 178 ctx = context.WithValue(ctx, ProvOutputKey, o) 179 ctx = context.WithValue(ctx, ProvRawStateKey, s) 180 return p.ApplyFunc(ctx) 181 } 182 183 // Validate implements the terraform.ResourceProvisioner interface. 184 func (p *Provisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) { 185 if err := p.InternalValidate(); err != nil { 186 return nil, []error{fmt.Errorf( 187 "Internal validation of the provisioner failed! This is always a bug\n"+ 188 "with the provisioner itself, and not a user issue. Please report\n"+ 189 "this bug:\n\n%s", err)} 190 } 191 192 if p.Schema != nil { 193 w, e := schemaMap(p.Schema).Validate(c) 194 ws = append(ws, w...) 195 es = append(es, e...) 196 } 197 198 if p.ValidateFunc != nil { 199 w, e := p.ValidateFunc(c) 200 ws = append(ws, w...) 201 es = append(es, e...) 202 } 203 204 return ws, es 205 }