github.com/hugorut/terraform@v1.1.3/src/backend/remote-state/http/backend.go (about) 1 package http 2 3 import ( 4 "context" 5 "crypto/tls" 6 "fmt" 7 "log" 8 "net/http" 9 "net/url" 10 "time" 11 12 "github.com/hashicorp/go-cleanhttp" 13 "github.com/hashicorp/go-retryablehttp" 14 "github.com/hugorut/terraform/src/backend" 15 "github.com/hugorut/terraform/src/legacy/helper/schema" 16 "github.com/hugorut/terraform/src/logging" 17 "github.com/hugorut/terraform/src/states/remote" 18 "github.com/hugorut/terraform/src/states/statemgr" 19 ) 20 21 func New() backend.Backend { 22 s := &schema.Backend{ 23 Schema: map[string]*schema.Schema{ 24 "address": &schema.Schema{ 25 Type: schema.TypeString, 26 Required: true, 27 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_ADDRESS", nil), 28 Description: "The address of the REST endpoint", 29 }, 30 "update_method": &schema.Schema{ 31 Type: schema.TypeString, 32 Optional: true, 33 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UPDATE_METHOD", "POST"), 34 Description: "HTTP method to use when updating state", 35 }, 36 "lock_address": &schema.Schema{ 37 Type: schema.TypeString, 38 Optional: true, 39 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_LOCK_ADDRESS", nil), 40 Description: "The address of the lock REST endpoint", 41 }, 42 "unlock_address": &schema.Schema{ 43 Type: schema.TypeString, 44 Optional: true, 45 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UNLOCK_ADDRESS", nil), 46 Description: "The address of the unlock REST endpoint", 47 }, 48 "lock_method": &schema.Schema{ 49 Type: schema.TypeString, 50 Optional: true, 51 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_LOCK_METHOD", "LOCK"), 52 Description: "The HTTP method to use when locking", 53 }, 54 "unlock_method": &schema.Schema{ 55 Type: schema.TypeString, 56 Optional: true, 57 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UNLOCK_METHOD", "UNLOCK"), 58 Description: "The HTTP method to use when unlocking", 59 }, 60 "username": &schema.Schema{ 61 Type: schema.TypeString, 62 Optional: true, 63 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_USERNAME", nil), 64 Description: "The username for HTTP basic authentication", 65 }, 66 "password": &schema.Schema{ 67 Type: schema.TypeString, 68 Optional: true, 69 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_PASSWORD", nil), 70 Description: "The password for HTTP basic authentication", 71 }, 72 "skip_cert_verification": &schema.Schema{ 73 Type: schema.TypeBool, 74 Optional: true, 75 Default: false, 76 Description: "Whether to skip TLS verification.", 77 }, 78 "retry_max": &schema.Schema{ 79 Type: schema.TypeInt, 80 Optional: true, 81 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_MAX", 2), 82 Description: "The number of HTTP request retries.", 83 }, 84 "retry_wait_min": &schema.Schema{ 85 Type: schema.TypeInt, 86 Optional: true, 87 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_WAIT_MIN", 1), 88 Description: "The minimum time in seconds to wait between HTTP request attempts.", 89 }, 90 "retry_wait_max": &schema.Schema{ 91 Type: schema.TypeInt, 92 Optional: true, 93 DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_WAIT_MAX", 30), 94 Description: "The maximum time in seconds to wait between HTTP request attempts.", 95 }, 96 }, 97 } 98 99 b := &Backend{Backend: s} 100 b.Backend.ConfigureFunc = b.configure 101 return b 102 } 103 104 type Backend struct { 105 *schema.Backend 106 107 client *httpClient 108 } 109 110 func (b *Backend) configure(ctx context.Context) error { 111 data := schema.FromContextBackendConfig(ctx) 112 113 address := data.Get("address").(string) 114 updateURL, err := url.Parse(address) 115 if err != nil { 116 return fmt.Errorf("failed to parse address URL: %s", err) 117 } 118 if updateURL.Scheme != "http" && updateURL.Scheme != "https" { 119 return fmt.Errorf("address must be HTTP or HTTPS") 120 } 121 122 updateMethod := data.Get("update_method").(string) 123 124 var lockURL *url.URL 125 if v, ok := data.GetOk("lock_address"); ok && v.(string) != "" { 126 var err error 127 lockURL, err = url.Parse(v.(string)) 128 if err != nil { 129 return fmt.Errorf("failed to parse lockAddress URL: %s", err) 130 } 131 if lockURL.Scheme != "http" && lockURL.Scheme != "https" { 132 return fmt.Errorf("lockAddress must be HTTP or HTTPS") 133 } 134 } 135 136 lockMethod := data.Get("lock_method").(string) 137 138 var unlockURL *url.URL 139 if v, ok := data.GetOk("unlock_address"); ok && v.(string) != "" { 140 var err error 141 unlockURL, err = url.Parse(v.(string)) 142 if err != nil { 143 return fmt.Errorf("failed to parse unlockAddress URL: %s", err) 144 } 145 if unlockURL.Scheme != "http" && unlockURL.Scheme != "https" { 146 return fmt.Errorf("unlockAddress must be HTTP or HTTPS") 147 } 148 } 149 150 unlockMethod := data.Get("unlock_method").(string) 151 152 client := cleanhttp.DefaultPooledClient() 153 154 if data.Get("skip_cert_verification").(bool) { 155 // ignores TLS verification 156 client.Transport.(*http.Transport).TLSClientConfig = &tls.Config{ 157 InsecureSkipVerify: true, 158 } 159 } 160 161 rClient := retryablehttp.NewClient() 162 rClient.HTTPClient = client 163 rClient.RetryMax = data.Get("retry_max").(int) 164 rClient.RetryWaitMin = time.Duration(data.Get("retry_wait_min").(int)) * time.Second 165 rClient.RetryWaitMax = time.Duration(data.Get("retry_wait_max").(int)) * time.Second 166 rClient.Logger = log.New(logging.LogOutput(), "", log.Flags()) 167 168 b.client = &httpClient{ 169 URL: updateURL, 170 UpdateMethod: updateMethod, 171 172 LockURL: lockURL, 173 LockMethod: lockMethod, 174 UnlockURL: unlockURL, 175 UnlockMethod: unlockMethod, 176 177 Username: data.Get("username").(string), 178 Password: data.Get("password").(string), 179 180 // accessible only for testing use 181 Client: rClient, 182 } 183 return nil 184 } 185 186 func (b *Backend) StateMgr(name string) (statemgr.Full, error) { 187 if name != backend.DefaultStateName { 188 return nil, backend.ErrWorkspacesNotSupported 189 } 190 191 return &remote.State{Client: b.client}, nil 192 } 193 194 func (b *Backend) Workspaces() ([]string, error) { 195 return nil, backend.ErrWorkspacesNotSupported 196 } 197 198 func (b *Backend) DeleteWorkspace(string) error { 199 return backend.ErrWorkspacesNotSupported 200 }