mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-18 12:42:58 -06:00
f40800b3a4
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
198 lines
5.8 KiB
Go
198 lines
5.8 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/hashicorp/go-cleanhttp"
|
|
"github.com/hashicorp/go-retryablehttp"
|
|
"github.com/hashicorp/terraform/internal/backend"
|
|
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
|
"github.com/hashicorp/terraform/internal/states/remote"
|
|
"github.com/hashicorp/terraform/internal/states/statemgr"
|
|
)
|
|
|
|
func New() backend.Backend {
|
|
s := &schema.Backend{
|
|
Schema: map[string]*schema.Schema{
|
|
"address": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_ADDRESS", nil),
|
|
Description: "The address of the REST endpoint",
|
|
},
|
|
"update_method": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UPDATE_METHOD", "POST"),
|
|
Description: "HTTP method to use when updating state",
|
|
},
|
|
"lock_address": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_LOCK_ADDRESS", nil),
|
|
Description: "The address of the lock REST endpoint",
|
|
},
|
|
"unlock_address": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UNLOCK_ADDRESS", nil),
|
|
Description: "The address of the unlock REST endpoint",
|
|
},
|
|
"lock_method": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_LOCK_METHOD", "LOCK"),
|
|
Description: "The HTTP method to use when locking",
|
|
},
|
|
"unlock_method": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_UNLOCK_METHOD", "UNLOCK"),
|
|
Description: "The HTTP method to use when unlocking",
|
|
},
|
|
"username": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_USERNAME", nil),
|
|
Description: "The username for HTTP basic authentication",
|
|
},
|
|
"password": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_PASSWORD", nil),
|
|
Description: "The password for HTTP basic authentication",
|
|
},
|
|
"skip_cert_verification": &schema.Schema{
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
Default: false,
|
|
Description: "Whether to skip TLS verification.",
|
|
},
|
|
"retry_max": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_MAX", 2),
|
|
Description: "The number of HTTP request retries.",
|
|
},
|
|
"retry_wait_min": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_WAIT_MIN", 1),
|
|
Description: "The minimum time in seconds to wait between HTTP request attempts.",
|
|
},
|
|
"retry_wait_max": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("TF_HTTP_RETRY_WAIT_MAX", 30),
|
|
Description: "The maximum time in seconds to wait between HTTP request attempts.",
|
|
},
|
|
},
|
|
}
|
|
|
|
b := &Backend{Backend: s}
|
|
b.Backend.ConfigureFunc = b.configure
|
|
return b
|
|
}
|
|
|
|
type Backend struct {
|
|
*schema.Backend
|
|
|
|
client *httpClient
|
|
}
|
|
|
|
func (b *Backend) configure(ctx context.Context) error {
|
|
data := schema.FromContextBackendConfig(ctx)
|
|
|
|
address := data.Get("address").(string)
|
|
updateURL, err := url.Parse(address)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse address URL: %s", err)
|
|
}
|
|
if updateURL.Scheme != "http" && updateURL.Scheme != "https" {
|
|
return fmt.Errorf("address must be HTTP or HTTPS")
|
|
}
|
|
|
|
updateMethod := data.Get("update_method").(string)
|
|
|
|
var lockURL *url.URL
|
|
if v, ok := data.GetOk("lock_address"); ok && v.(string) != "" {
|
|
var err error
|
|
lockURL, err = url.Parse(v.(string))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse lockAddress URL: %s", err)
|
|
}
|
|
if lockURL.Scheme != "http" && lockURL.Scheme != "https" {
|
|
return fmt.Errorf("lockAddress must be HTTP or HTTPS")
|
|
}
|
|
}
|
|
|
|
lockMethod := data.Get("lock_method").(string)
|
|
|
|
var unlockURL *url.URL
|
|
if v, ok := data.GetOk("unlock_address"); ok && v.(string) != "" {
|
|
var err error
|
|
unlockURL, err = url.Parse(v.(string))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse unlockAddress URL: %s", err)
|
|
}
|
|
if unlockURL.Scheme != "http" && unlockURL.Scheme != "https" {
|
|
return fmt.Errorf("unlockAddress must be HTTP or HTTPS")
|
|
}
|
|
}
|
|
|
|
unlockMethod := data.Get("unlock_method").(string)
|
|
|
|
client := cleanhttp.DefaultPooledClient()
|
|
|
|
if data.Get("skip_cert_verification").(bool) {
|
|
// ignores TLS verification
|
|
client.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
}
|
|
}
|
|
|
|
rClient := retryablehttp.NewClient()
|
|
rClient.HTTPClient = client
|
|
rClient.RetryMax = data.Get("retry_max").(int)
|
|
rClient.RetryWaitMin = time.Duration(data.Get("retry_wait_min").(int)) * time.Second
|
|
rClient.RetryWaitMax = time.Duration(data.Get("retry_wait_max").(int)) * time.Second
|
|
|
|
b.client = &httpClient{
|
|
URL: updateURL,
|
|
UpdateMethod: updateMethod,
|
|
|
|
LockURL: lockURL,
|
|
LockMethod: lockMethod,
|
|
UnlockURL: unlockURL,
|
|
UnlockMethod: unlockMethod,
|
|
|
|
Username: data.Get("username").(string),
|
|
Password: data.Get("password").(string),
|
|
|
|
// accessible only for testing use
|
|
Client: rClient,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
|
|
if name != backend.DefaultStateName {
|
|
return nil, backend.ErrWorkspacesNotSupported
|
|
}
|
|
|
|
return &remote.State{Client: b.client}, nil
|
|
}
|
|
|
|
func (b *Backend) Workspaces() ([]string, error) {
|
|
return nil, backend.ErrWorkspacesNotSupported
|
|
}
|
|
|
|
func (b *Backend) DeleteWorkspace(string) error {
|
|
return backend.ErrWorkspacesNotSupported
|
|
}
|