mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
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.
486 lines
14 KiB
Go
486 lines
14 KiB
Go
package swift
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gophercloud/gophercloud"
|
|
"github.com/gophercloud/utils/terraform/auth"
|
|
|
|
"github.com/hashicorp/terraform/internal/backend"
|
|
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
|
"github.com/hashicorp/terraform/version"
|
|
)
|
|
|
|
// Use openstackbase.Config as the base/foundation of this provider's
|
|
// Config struct.
|
|
type Config struct {
|
|
auth.Config
|
|
}
|
|
|
|
// New creates a new backend for Swift remote state.
|
|
func New() backend.Backend {
|
|
s := &schema.Backend{
|
|
Schema: map[string]*schema.Schema{
|
|
"auth_url": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_AUTH_URL", ""),
|
|
Description: descriptions["auth_url"],
|
|
},
|
|
|
|
"region_name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Description: descriptions["region_name"],
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
|
|
},
|
|
|
|
"user_name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_USERNAME", ""),
|
|
Description: descriptions["user_name"],
|
|
},
|
|
|
|
"user_id": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_USER_ID", ""),
|
|
Description: descriptions["user_name"],
|
|
},
|
|
|
|
"application_credential_id": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_ID", ""),
|
|
Description: descriptions["application_credential_id"],
|
|
},
|
|
|
|
"application_credential_name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_NAME", ""),
|
|
Description: descriptions["application_credential_name"],
|
|
},
|
|
|
|
"application_credential_secret": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_SECRET", ""),
|
|
Description: descriptions["application_credential_secret"],
|
|
},
|
|
|
|
"tenant_id": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
|
|
"OS_TENANT_ID",
|
|
"OS_PROJECT_ID",
|
|
}, ""),
|
|
Description: descriptions["tenant_id"],
|
|
},
|
|
|
|
"tenant_name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
|
|
"OS_TENANT_NAME",
|
|
"OS_PROJECT_NAME",
|
|
}, ""),
|
|
Description: descriptions["tenant_name"],
|
|
},
|
|
|
|
"password": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Sensitive: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_PASSWORD", ""),
|
|
Description: descriptions["password"],
|
|
},
|
|
|
|
"token": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
|
|
"OS_TOKEN",
|
|
"OS_AUTH_TOKEN",
|
|
}, ""),
|
|
Description: descriptions["token"],
|
|
},
|
|
|
|
"user_domain_name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_USER_DOMAIN_NAME", ""),
|
|
Description: descriptions["user_domain_name"],
|
|
},
|
|
|
|
"user_domain_id": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_USER_DOMAIN_ID", ""),
|
|
Description: descriptions["user_domain_id"],
|
|
},
|
|
|
|
"project_domain_name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_PROJECT_DOMAIN_NAME", ""),
|
|
Description: descriptions["project_domain_name"],
|
|
},
|
|
|
|
"project_domain_id": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_PROJECT_DOMAIN_ID", ""),
|
|
Description: descriptions["project_domain_id"],
|
|
},
|
|
|
|
"domain_id": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_DOMAIN_ID", ""),
|
|
Description: descriptions["domain_id"],
|
|
},
|
|
|
|
"domain_name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_DOMAIN_NAME", ""),
|
|
Description: descriptions["domain_name"],
|
|
},
|
|
|
|
"default_domain": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_DEFAULT_DOMAIN", "default"),
|
|
Description: descriptions["default_domain"],
|
|
},
|
|
|
|
"insecure": {
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_INSECURE", nil),
|
|
Description: descriptions["insecure"],
|
|
},
|
|
|
|
"endpoint_type": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_ENDPOINT_TYPE", ""),
|
|
},
|
|
|
|
"cacert_file": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_CACERT", ""),
|
|
Description: descriptions["cacert_file"],
|
|
},
|
|
|
|
"cert": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_CERT", ""),
|
|
Description: descriptions["cert"],
|
|
},
|
|
|
|
"key": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_KEY", ""),
|
|
Description: descriptions["key"],
|
|
},
|
|
|
|
"swauth": {
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_SWAUTH", false),
|
|
Description: descriptions["swauth"],
|
|
},
|
|
|
|
"allow_reauth": {
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_ALLOW_REAUTH", false),
|
|
Description: descriptions["allow_reauth"],
|
|
},
|
|
|
|
"cloud": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_CLOUD", ""),
|
|
Description: descriptions["cloud"],
|
|
},
|
|
|
|
"max_retries": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
Default: 0,
|
|
Description: descriptions["max_retries"],
|
|
},
|
|
|
|
"disable_no_cache_header": {
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
Default: false,
|
|
Description: descriptions["disable_no_cache_header"],
|
|
},
|
|
|
|
"path": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Description: descriptions["path"],
|
|
Deprecated: "Use container instead",
|
|
ConflictsWith: []string{"container"},
|
|
},
|
|
|
|
"container": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Description: descriptions["container"],
|
|
},
|
|
|
|
"archive_path": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Description: descriptions["archive_path"],
|
|
Deprecated: "Use archive_container instead",
|
|
ConflictsWith: []string{"archive_container"},
|
|
},
|
|
|
|
"archive_container": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Description: descriptions["archive_container"],
|
|
},
|
|
|
|
"expire_after": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Description: descriptions["expire_after"],
|
|
},
|
|
|
|
"lock": {
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
Description: "Lock state access",
|
|
Default: true,
|
|
},
|
|
|
|
"state_name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Description: descriptions["state_name"],
|
|
Default: "tfstate.tf",
|
|
},
|
|
},
|
|
}
|
|
|
|
result := &Backend{Backend: s}
|
|
result.Backend.ConfigureFunc = result.configure
|
|
return result
|
|
}
|
|
|
|
var descriptions map[string]string
|
|
|
|
func init() {
|
|
descriptions = map[string]string{
|
|
"auth_url": "The Identity authentication URL.",
|
|
|
|
"region_name": "The name of the Region to use.",
|
|
|
|
"user_name": "Username to login with.",
|
|
|
|
"user_id": "User ID to login with.",
|
|
|
|
"application_credential_id": "Application Credential ID to login with.",
|
|
|
|
"application_credential_name": "Application Credential name to login with.",
|
|
|
|
"application_credential_secret": "Application Credential secret to login with.",
|
|
|
|
"tenant_id": "The ID of the Tenant (Identity v2) or Project (Identity v3)\n" +
|
|
"to login with.",
|
|
|
|
"tenant_name": "The name of the Tenant (Identity v2) or Project (Identity v3)\n" +
|
|
"to login with.",
|
|
|
|
"password": "Password to login with.",
|
|
|
|
"token": "Authentication token to use as an alternative to username/password.",
|
|
|
|
"user_domain_name": "The name of the domain where the user resides (Identity v3).",
|
|
|
|
"user_domain_id": "The ID of the domain where the user resides (Identity v3).",
|
|
|
|
"project_domain_name": "The name of the domain where the project resides (Identity v3).",
|
|
|
|
"project_domain_id": "The ID of the domain where the proejct resides (Identity v3).",
|
|
|
|
"domain_id": "The ID of the Domain to scope to (Identity v3).",
|
|
|
|
"domain_name": "The name of the Domain to scope to (Identity v3).",
|
|
|
|
"default_domain": "The name of the Domain ID to scope to if no other domain is specified. Defaults to `default` (Identity v3).",
|
|
|
|
"insecure": "Trust self-signed certificates.",
|
|
|
|
"cacert_file": "A Custom CA certificate.",
|
|
|
|
"endpoint_type": "The catalog endpoint type to use.",
|
|
|
|
"cert": "A client certificate to authenticate with.",
|
|
|
|
"key": "A client private key to authenticate with.",
|
|
|
|
"swauth": "Use Swift's authentication system instead of Keystone.",
|
|
|
|
"allow_reauth": "If set to `true`, OpenStack authorization will be perfomed\n" +
|
|
"automatically, if the initial auth token get expired. This is useful,\n" +
|
|
"when the token TTL is low or the overall Terraform provider execution\n" +
|
|
"time expected to be greater than the initial token TTL.",
|
|
|
|
"cloud": "An entry in a `clouds.yaml` file to use.",
|
|
|
|
"max_retries": "How many times HTTP connection should be retried until giving up.",
|
|
|
|
"disable_no_cache_header": "If set to `true`, the HTTP `Cache-Control: no-cache` header will not be added by default to all API requests.",
|
|
|
|
"path": "Swift container path to use.",
|
|
|
|
"container": "Swift container to create",
|
|
|
|
"archive_path": "Swift container path to archive state to.",
|
|
|
|
"archive_container": "Swift container to archive state to.",
|
|
|
|
"expire_after": "Archive object expiry duration.",
|
|
|
|
"state_name": "Name of state object in container",
|
|
}
|
|
}
|
|
|
|
type Backend struct {
|
|
*schema.Backend
|
|
|
|
// Fields below are set from configure
|
|
client *gophercloud.ServiceClient
|
|
archive bool
|
|
archiveContainer string
|
|
expireSecs int
|
|
container string
|
|
lock bool
|
|
stateName string
|
|
}
|
|
|
|
func (b *Backend) configure(ctx context.Context) error {
|
|
if b.client != nil {
|
|
return nil
|
|
}
|
|
|
|
// Grab the resource data
|
|
data := schema.FromContextBackendConfig(ctx)
|
|
config := &Config{
|
|
auth.Config{
|
|
CACertFile: data.Get("cacert_file").(string),
|
|
ClientCertFile: data.Get("cert").(string),
|
|
ClientKeyFile: data.Get("key").(string),
|
|
Cloud: data.Get("cloud").(string),
|
|
DefaultDomain: data.Get("default_domain").(string),
|
|
DomainID: data.Get("domain_id").(string),
|
|
DomainName: data.Get("domain_name").(string),
|
|
EndpointType: data.Get("endpoint_type").(string),
|
|
IdentityEndpoint: data.Get("auth_url").(string),
|
|
Password: data.Get("password").(string),
|
|
ProjectDomainID: data.Get("project_domain_id").(string),
|
|
ProjectDomainName: data.Get("project_domain_name").(string),
|
|
Region: data.Get("region_name").(string),
|
|
Swauth: data.Get("swauth").(bool),
|
|
Token: data.Get("token").(string),
|
|
TenantID: data.Get("tenant_id").(string),
|
|
TenantName: data.Get("tenant_name").(string),
|
|
UserDomainID: data.Get("user_domain_id").(string),
|
|
UserDomainName: data.Get("user_domain_name").(string),
|
|
Username: data.Get("user_name").(string),
|
|
UserID: data.Get("user_id").(string),
|
|
ApplicationCredentialID: data.Get("application_credential_id").(string),
|
|
ApplicationCredentialName: data.Get("application_credential_name").(string),
|
|
ApplicationCredentialSecret: data.Get("application_credential_secret").(string),
|
|
AllowReauth: data.Get("allow_reauth").(bool),
|
|
MaxRetries: data.Get("max_retries").(int),
|
|
DisableNoCacheHeader: data.Get("disable_no_cache_header").(bool),
|
|
TerraformVersion: version.Version,
|
|
},
|
|
}
|
|
|
|
if v, ok := data.GetOkExists("insecure"); ok {
|
|
insecure := v.(bool)
|
|
config.Insecure = &insecure
|
|
}
|
|
|
|
if err := config.LoadAndValidate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Assign state name
|
|
b.stateName = data.Get("state_name").(string)
|
|
|
|
// Assign Container
|
|
b.container = data.Get("container").(string)
|
|
if b.container == "" {
|
|
// Check deprecated field
|
|
b.container = data.Get("path").(string)
|
|
}
|
|
|
|
// Store the lock information
|
|
b.lock = data.Get("lock").(bool)
|
|
|
|
// Enable object archiving?
|
|
if archiveContainer, ok := data.GetOk("archive_container"); ok {
|
|
log.Printf("[DEBUG] Archive_container set, enabling object versioning")
|
|
b.archive = true
|
|
b.archiveContainer = archiveContainer.(string)
|
|
} else if archivePath, ok := data.GetOk("archive_path"); ok {
|
|
log.Printf("[DEBUG] Archive_path set, enabling object versioning")
|
|
b.archive = true
|
|
b.archiveContainer = archivePath.(string)
|
|
}
|
|
|
|
// Enable object expiry?
|
|
if expireRaw, ok := data.GetOk("expire_after"); ok {
|
|
expire := expireRaw.(string)
|
|
log.Printf("[DEBUG] Requested that remote state expires after %s", expire)
|
|
|
|
if strings.HasSuffix(expire, "d") {
|
|
log.Printf("[DEBUG] Got a days expire after duration. Converting to hours")
|
|
days, err := strconv.Atoi(expire[:len(expire)-1])
|
|
if err != nil {
|
|
return fmt.Errorf("Error converting expire_after value %s to int: %s", expire, err)
|
|
}
|
|
|
|
expire = fmt.Sprintf("%dh", days*24)
|
|
log.Printf("[DEBUG] Expire after %s hours", expire)
|
|
}
|
|
|
|
expireDur, err := time.ParseDuration(expire)
|
|
if err != nil {
|
|
log.Printf("[DEBUG] Error parsing duration %s: %s", expire, err)
|
|
return fmt.Errorf("Error parsing expire_after duration '%s': %s", expire, err)
|
|
}
|
|
log.Printf("[DEBUG] Seconds duration = %d", int(expireDur.Seconds()))
|
|
b.expireSecs = int(expireDur.Seconds())
|
|
}
|
|
|
|
var err error
|
|
if b.client, err = config.ObjectStorageV1Client(config.Region); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|