mirror of https://github.com/hashicorp/terraform
parent
dfcd178f8f
commit
bfc6b4fc25
@ -0,0 +1,166 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/state/remote"
|
||||
)
|
||||
|
||||
func New() backend.Backend {
|
||||
s := &schema.Backend{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"address": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "The address of the REST endpoint",
|
||||
},
|
||||
"update_method": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "POST",
|
||||
Description: "HTTP method to use when updating state",
|
||||
},
|
||||
"lock_address": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The address of the lock REST endpoint",
|
||||
},
|
||||
"unlock_address": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The address of the unlock REST endpoint",
|
||||
},
|
||||
"lock_method": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "LOCK",
|
||||
Description: "The HTTP method to use when locking",
|
||||
},
|
||||
"unlock_method": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "UNLOCK",
|
||||
Description: "The HTTP method to use when unlocking",
|
||||
},
|
||||
"username": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The username for HTTP basic authentication",
|
||||
},
|
||||
"password": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
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.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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: client,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) State(name string) (state.State, error) {
|
||||
if name != backend.DefaultStateName {
|
||||
return nil, backend.ErrNamedStatesNotSupported
|
||||
}
|
||||
|
||||
return &remote.State{Client: b.client}, nil
|
||||
}
|
||||
|
||||
func (b *Backend) States() ([]string, error) {
|
||||
return nil, backend.ErrNamedStatesNotSupported
|
||||
}
|
||||
|
||||
func (b *Backend) DeleteState(string) error {
|
||||
return backend.ErrNamedStatesNotSupported
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
)
|
||||
|
||||
func TestBackend_impl(t *testing.T) {
|
||||
var _ backend.Backend = new(Backend)
|
||||
}
|
||||
|
||||
func TestHTTPClientFactory(t *testing.T) {
|
||||
// defaults
|
||||
|
||||
conf := map[string]interface{}{
|
||||
"address": "http://127.0.0.1:8888/foo",
|
||||
}
|
||||
b := backend.TestBackendConfig(t, New(), conf).(*Backend)
|
||||
client := b.client
|
||||
|
||||
if client == nil {
|
||||
t.Fatal("Unexpected failure, address")
|
||||
}
|
||||
if client.URL.String() != conf["address"] {
|
||||
t.Fatalf("Expected address \"%s\", got \"%s\"", conf["address"], client.URL.String())
|
||||
}
|
||||
if client.UpdateMethod != "POST" {
|
||||
t.Fatalf("Expected update_method \"%s\", got \"%s\"", "POST", client.UpdateMethod)
|
||||
}
|
||||
if client.LockURL != nil || client.LockMethod != "LOCK" {
|
||||
t.Fatal("Unexpected lock_address or lock_method")
|
||||
}
|
||||
if client.UnlockURL != nil || client.UnlockMethod != "UNLOCK" {
|
||||
t.Fatal("Unexpected unlock_address or unlock_method")
|
||||
}
|
||||
if client.Username != "" || client.Password != "" {
|
||||
t.Fatal("Unexpected username or password")
|
||||
}
|
||||
|
||||
// custom
|
||||
conf = map[string]interface{}{
|
||||
"address": "http://127.0.0.1:8888/foo",
|
||||
"update_method": "BLAH",
|
||||
"lock_address": "http://127.0.0.1:8888/bar",
|
||||
"lock_method": "BLIP",
|
||||
"unlock_address": "http://127.0.0.1:8888/baz",
|
||||
"unlock_method": "BLOOP",
|
||||
"username": "user",
|
||||
"password": "pass",
|
||||
}
|
||||
|
||||
b = backend.TestBackendConfig(t, New(), conf).(*Backend)
|
||||
client = b.client
|
||||
|
||||
if client == nil {
|
||||
t.Fatal("Unexpected failure, update_method")
|
||||
}
|
||||
if client.UpdateMethod != "BLAH" {
|
||||
t.Fatalf("Expected update_method \"%s\", got \"%s\"", "BLAH", client.UpdateMethod)
|
||||
}
|
||||
if client.LockURL.String() != conf["lock_address"] || client.LockMethod != "BLIP" {
|
||||
t.Fatalf("Unexpected lock_address \"%s\" vs \"%s\" or lock_method \"%s\" vs \"%s\"", client.LockURL.String(),
|
||||
conf["lock_address"], client.LockMethod, conf["lock_method"])
|
||||
}
|
||||
if client.UnlockURL.String() != conf["unlock_address"] || client.UnlockMethod != "BLOOP" {
|
||||
t.Fatalf("Unexpected unlock_address \"%s\" vs \"%s\" or unlock_method \"%s\" vs \"%s\"", client.UnlockURL.String(),
|
||||
conf["unlock_address"], client.UnlockMethod, conf["unlock_method"])
|
||||
}
|
||||
if client.Username != "user" || client.Password != "pass" {
|
||||
t.Fatalf("Unexpected username \"%s\" vs \"%s\" or password \"%s\" vs \"%s\"", client.Username, conf["username"],
|
||||
client.Password, conf["password"])
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue