github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/backend/remote-state/swift/backend.go (about)

     1  package swift
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/gophercloud/gophercloud"
    12  	"github.com/gophercloud/gophercloud/openstack"
    13  
    14  	"github.com/hashicorp/terraform/backend"
    15  	"github.com/hashicorp/terraform/helper/schema"
    16  	tf_openstack "github.com/terraform-providers/terraform-provider-openstack/openstack"
    17  )
    18  
    19  // New creates a new backend for Swift remote state.
    20  func New() backend.Backend {
    21  	s := &schema.Backend{
    22  		Schema: map[string]*schema.Schema{
    23  			"auth_url": &schema.Schema{
    24  				Type:        schema.TypeString,
    25  				Required:    true,
    26  				DefaultFunc: schema.EnvDefaultFunc("OS_AUTH_URL", ""),
    27  				Description: descriptions["auth_url"],
    28  			},
    29  
    30  			"user_id": &schema.Schema{
    31  				Type:        schema.TypeString,
    32  				Optional:    true,
    33  				DefaultFunc: schema.EnvDefaultFunc("OS_USER_ID", ""),
    34  				Description: descriptions["user_name"],
    35  			},
    36  
    37  			"user_name": &schema.Schema{
    38  				Type:        schema.TypeString,
    39  				Optional:    true,
    40  				DefaultFunc: schema.EnvDefaultFunc("OS_USERNAME", ""),
    41  				Description: descriptions["user_name"],
    42  			},
    43  
    44  			"application_credential_id": &schema.Schema{
    45  				Type:        schema.TypeString,
    46  				Optional:    true,
    47  				DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_ID", ""),
    48  				Description: descriptions["application_credential_id"],
    49  			},
    50  
    51  			"application_credential_name": &schema.Schema{
    52  				Type:        schema.TypeString,
    53  				Optional:    true,
    54  				DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_NAME", ""),
    55  				Description: descriptions["application_credential_name"],
    56  			},
    57  
    58  			"application_credential_secret": &schema.Schema{
    59  				Type:        schema.TypeString,
    60  				Optional:    true,
    61  				DefaultFunc: schema.EnvDefaultFunc("OS_APPLICATION_CREDENTIAL_SECRET", ""),
    62  				Description: descriptions["application_credential_secret"],
    63  			},
    64  
    65  			"tenant_id": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Optional: true,
    68  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
    69  					"OS_TENANT_ID",
    70  					"OS_PROJECT_ID",
    71  				}, ""),
    72  				Description: descriptions["tenant_id"],
    73  			},
    74  
    75  			"tenant_name": &schema.Schema{
    76  				Type:     schema.TypeString,
    77  				Optional: true,
    78  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
    79  					"OS_TENANT_NAME",
    80  					"OS_PROJECT_NAME",
    81  				}, ""),
    82  				Description: descriptions["tenant_name"],
    83  			},
    84  
    85  			"password": &schema.Schema{
    86  				Type:        schema.TypeString,
    87  				Optional:    true,
    88  				Sensitive:   true,
    89  				DefaultFunc: schema.EnvDefaultFunc("OS_PASSWORD", ""),
    90  				Description: descriptions["password"],
    91  			},
    92  
    93  			"token": &schema.Schema{
    94  				Type:     schema.TypeString,
    95  				Optional: true,
    96  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
    97  					"OS_TOKEN",
    98  					"OS_AUTH_TOKEN",
    99  				}, ""),
   100  				Description: descriptions["token"],
   101  			},
   102  
   103  			"user_domain_name": &schema.Schema{
   104  				Type:        schema.TypeString,
   105  				Optional:    true,
   106  				DefaultFunc: schema.EnvDefaultFunc("OS_USER_DOMAIN_NAME", ""),
   107  				Description: descriptions["user_domain_name"],
   108  			},
   109  
   110  			"user_domain_id": &schema.Schema{
   111  				Type:        schema.TypeString,
   112  				Optional:    true,
   113  				DefaultFunc: schema.EnvDefaultFunc("OS_USER_DOMAIN_ID", ""),
   114  				Description: descriptions["user_domain_id"],
   115  			},
   116  
   117  			"project_domain_name": &schema.Schema{
   118  				Type:        schema.TypeString,
   119  				Optional:    true,
   120  				DefaultFunc: schema.EnvDefaultFunc("OS_PROJECT_DOMAIN_NAME", ""),
   121  				Description: descriptions["project_domain_name"],
   122  			},
   123  
   124  			"project_domain_id": &schema.Schema{
   125  				Type:        schema.TypeString,
   126  				Optional:    true,
   127  				DefaultFunc: schema.EnvDefaultFunc("OS_PROJECT_DOMAIN_ID", ""),
   128  				Description: descriptions["project_domain_id"],
   129  			},
   130  
   131  			"domain_id": &schema.Schema{
   132  				Type:        schema.TypeString,
   133  				Optional:    true,
   134  				DefaultFunc: schema.EnvDefaultFunc("OS_DOMAIN_ID", ""),
   135  				Description: descriptions["domain_id"],
   136  			},
   137  
   138  			"domain_name": &schema.Schema{
   139  				Type:        schema.TypeString,
   140  				Optional:    true,
   141  				DefaultFunc: schema.EnvDefaultFunc("OS_DOMAIN_NAME", ""),
   142  				Description: descriptions["domain_name"],
   143  			},
   144  
   145  			"default_domain": &schema.Schema{
   146  				Type:        schema.TypeString,
   147  				Optional:    true,
   148  				DefaultFunc: schema.EnvDefaultFunc("OS_DEFAULT_DOMAIN", "default"),
   149  				Description: descriptions["default_domain"],
   150  			},
   151  
   152  			"cloud": &schema.Schema{
   153  				Type:        schema.TypeString,
   154  				Optional:    true,
   155  				DefaultFunc: schema.EnvDefaultFunc("OS_CLOUD", ""),
   156  				Description: descriptions["cloud"],
   157  			},
   158  
   159  			"region_name": &schema.Schema{
   160  				Type:        schema.TypeString,
   161  				Required:    true,
   162  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
   163  				Description: descriptions["region_name"],
   164  			},
   165  
   166  			"insecure": &schema.Schema{
   167  				Type:        schema.TypeBool,
   168  				Optional:    true,
   169  				DefaultFunc: schema.EnvDefaultFunc("OS_INSECURE", nil),
   170  				Description: descriptions["insecure"],
   171  			},
   172  
   173  			"endpoint_type": &schema.Schema{
   174  				Type:        schema.TypeString,
   175  				Optional:    true,
   176  				DefaultFunc: schema.EnvDefaultFunc("OS_ENDPOINT_TYPE", ""),
   177  			},
   178  
   179  			"cacert_file": &schema.Schema{
   180  				Type:        schema.TypeString,
   181  				Optional:    true,
   182  				DefaultFunc: schema.EnvDefaultFunc("OS_CACERT", ""),
   183  				Description: descriptions["cacert_file"],
   184  			},
   185  
   186  			"cert": &schema.Schema{
   187  				Type:        schema.TypeString,
   188  				Optional:    true,
   189  				DefaultFunc: schema.EnvDefaultFunc("OS_CERT", ""),
   190  				Description: descriptions["cert"],
   191  			},
   192  
   193  			"key": &schema.Schema{
   194  				Type:        schema.TypeString,
   195  				Optional:    true,
   196  				DefaultFunc: schema.EnvDefaultFunc("OS_KEY", ""),
   197  				Description: descriptions["key"],
   198  			},
   199  
   200  			"path": &schema.Schema{
   201  				Type:          schema.TypeString,
   202  				Optional:      true,
   203  				Description:   descriptions["path"],
   204  				Deprecated:    "Use container instead",
   205  				ConflictsWith: []string{"container"},
   206  			},
   207  
   208  			"container": &schema.Schema{
   209  				Type:        schema.TypeString,
   210  				Optional:    true,
   211  				Description: descriptions["container"],
   212  			},
   213  
   214  			"archive_path": &schema.Schema{
   215  				Type:          schema.TypeString,
   216  				Optional:      true,
   217  				Description:   descriptions["archive_path"],
   218  				Deprecated:    "Use archive_container instead",
   219  				ConflictsWith: []string{"archive_container"},
   220  			},
   221  
   222  			"archive_container": &schema.Schema{
   223  				Type:        schema.TypeString,
   224  				Optional:    true,
   225  				Description: descriptions["archive_container"],
   226  			},
   227  
   228  			"expire_after": &schema.Schema{
   229  				Type:        schema.TypeString,
   230  				Optional:    true,
   231  				Description: descriptions["expire_after"],
   232  			},
   233  
   234  			"lock": &schema.Schema{
   235  				Type:        schema.TypeBool,
   236  				Optional:    true,
   237  				Description: "Lock state access",
   238  				Default:     true,
   239  			},
   240  
   241  			"state_name": &schema.Schema{
   242  				Type:        schema.TypeString,
   243  				Optional:    true,
   244  				Description: descriptions["state_name"],
   245  				Default:     "tfstate.tf",
   246  			},
   247  		},
   248  	}
   249  
   250  	result := &Backend{Backend: s}
   251  	result.Backend.ConfigureFunc = result.configure
   252  	return result
   253  }
   254  
   255  var descriptions map[string]string
   256  
   257  func init() {
   258  	descriptions = map[string]string{
   259  		"auth_url": "The Identity authentication URL.",
   260  
   261  		"user_name": "Username to login with.",
   262  
   263  		"user_id": "User ID to login with.",
   264  
   265  		"application_credential_id": "Application Credential ID to login with.",
   266  
   267  		"application_credential_name": "Application Credential name to login with.",
   268  
   269  		"application_credential_secret": "Application Credential secret to login with.",
   270  
   271  		"tenant_id": "The ID of the Tenant (Identity v2) or Project (Identity v3)\n" +
   272  			"to login with.",
   273  
   274  		"tenant_name": "The name of the Tenant (Identity v2) or Project (Identity v3)\n" +
   275  			"to login with.",
   276  
   277  		"password": "Password to login with.",
   278  
   279  		"token": "Authentication token to use as an alternative to username/password.",
   280  
   281  		"user_domain_name": "The name of the domain where the user resides (Identity v3).",
   282  
   283  		"user_domain_id": "The ID of the domain where the user resides (Identity v3).",
   284  
   285  		"project_domain_name": "The name of the domain where the project resides (Identity v3).",
   286  
   287  		"project_domain_id": "The ID of the domain where the proejct resides (Identity v3).",
   288  
   289  		"domain_id": "The ID of the Domain to scope to (Identity v3).",
   290  
   291  		"domain_name": "The name of the Domain to scope to (Identity v3).",
   292  
   293  		"default_domain": "The name of the Domain ID to scope to if no other domain is specified. Defaults to `default` (Identity v3).",
   294  
   295  		"cloud": "An entry in a `clouds.yaml` file to use.",
   296  
   297  		"region_name": "The name of the Region to use.",
   298  
   299  		"insecure": "Trust self-signed certificates.",
   300  
   301  		"cacert_file": "A Custom CA certificate.",
   302  
   303  		"endpoint_type": "The catalog endpoint type to use.",
   304  
   305  		"cert": "A client certificate to authenticate with.",
   306  
   307  		"key": "A client private key to authenticate with.",
   308  
   309  		"path": "Swift container path to use.",
   310  
   311  		"container": "Swift container to create",
   312  
   313  		"archive_path": "Swift container path to archive state to.",
   314  
   315  		"archive_container": "Swift container to archive state to.",
   316  
   317  		"expire_after": "Archive object expiry duration.",
   318  
   319  		"state_name": "Name of state object in container",
   320  	}
   321  }
   322  
   323  type Backend struct {
   324  	*schema.Backend
   325  
   326  	// Fields below are set from configure
   327  	client           *gophercloud.ServiceClient
   328  	archive          bool
   329  	archiveContainer string
   330  	expireSecs       int
   331  	container        string
   332  	lock             bool
   333  	stateName        string
   334  }
   335  
   336  func (b *Backend) configure(ctx context.Context) error {
   337  	if b.client != nil {
   338  		return nil
   339  	}
   340  
   341  	// Grab the resource data
   342  	data := schema.FromContextBackendConfig(ctx)
   343  	config := &tf_openstack.Config{
   344  		CACertFile:                  data.Get("cacert_file").(string),
   345  		ClientCertFile:              data.Get("cert").(string),
   346  		ClientKeyFile:               data.Get("key").(string),
   347  		Cloud:                       data.Get("cloud").(string),
   348  		DefaultDomain:               data.Get("default_domain").(string),
   349  		DomainID:                    data.Get("domain_id").(string),
   350  		DomainName:                  data.Get("domain_name").(string),
   351  		EndpointType:                data.Get("endpoint_type").(string),
   352  		IdentityEndpoint:            data.Get("auth_url").(string),
   353  		Password:                    data.Get("password").(string),
   354  		ProjectDomainID:             data.Get("project_domain_id").(string),
   355  		ProjectDomainName:           data.Get("project_domain_name").(string),
   356  		Token:                       data.Get("token").(string),
   357  		TenantID:                    data.Get("tenant_id").(string),
   358  		TenantName:                  data.Get("tenant_name").(string),
   359  		UserDomainID:                data.Get("user_domain_id").(string),
   360  		UserDomainName:              data.Get("user_domain_name").(string),
   361  		Username:                    data.Get("user_name").(string),
   362  		UserID:                      data.Get("user_id").(string),
   363  		ApplicationCredentialID:     data.Get("application_credential_id").(string),
   364  		ApplicationCredentialName:   data.Get("application_credential_name").(string),
   365  		ApplicationCredentialSecret: data.Get("application_credential_secret").(string),
   366  	}
   367  
   368  	if v, ok := data.GetOkExists("insecure"); ok {
   369  		insecure := v.(bool)
   370  		config.Insecure = &insecure
   371  	}
   372  
   373  	if err := config.LoadAndValidate(); err != nil {
   374  		return err
   375  	}
   376  
   377  	// Assign state name
   378  	b.stateName = data.Get("state_name").(string)
   379  
   380  	// Assign Container
   381  	b.container = data.Get("container").(string)
   382  	if b.container == "" {
   383  		// Check deprecated field
   384  		b.container = data.Get("path").(string)
   385  	}
   386  
   387  	// Store the lock information
   388  	b.lock = data.Get("lock").(bool)
   389  
   390  	// Enable object archiving?
   391  	if archiveContainer, ok := data.GetOk("archive_container"); ok {
   392  		log.Printf("[DEBUG] Archive_container set, enabling object versioning")
   393  		b.archive = true
   394  		b.archiveContainer = archiveContainer.(string)
   395  	} else if archivePath, ok := data.GetOk("archive_path"); ok {
   396  		log.Printf("[DEBUG] Archive_path set, enabling object versioning")
   397  		b.archive = true
   398  		b.archiveContainer = archivePath.(string)
   399  	}
   400  
   401  	// Enable object expiry?
   402  	if expireRaw, ok := data.GetOk("expire_after"); ok {
   403  		expire := expireRaw.(string)
   404  		log.Printf("[DEBUG] Requested that remote state expires after %s", expire)
   405  
   406  		if strings.HasSuffix(expire, "d") {
   407  			log.Printf("[DEBUG] Got a days expire after duration. Converting to hours")
   408  			days, err := strconv.Atoi(expire[:len(expire)-1])
   409  			if err != nil {
   410  				return fmt.Errorf("Error converting expire_after value %s to int: %s", expire, err)
   411  			}
   412  
   413  			expire = fmt.Sprintf("%dh", days*24)
   414  			log.Printf("[DEBUG] Expire after %s hours", expire)
   415  		}
   416  
   417  		expireDur, err := time.ParseDuration(expire)
   418  		if err != nil {
   419  			log.Printf("[DEBUG] Error parsing duration %s: %s", expire, err)
   420  			return fmt.Errorf("Error parsing expire_after duration '%s': %s", expire, err)
   421  		}
   422  		log.Printf("[DEBUG] Seconds duration = %d", int(expireDur.Seconds()))
   423  		b.expireSecs = int(expireDur.Seconds())
   424  	}
   425  
   426  	objClient, err := openstack.NewObjectStorageV1(config.OsClient, gophercloud.EndpointOpts{
   427  		Region: data.Get("region_name").(string),
   428  	})
   429  	if err != nil {
   430  		return err
   431  	}
   432  
   433  	b.client = objClient
   434  
   435  	return nil
   436  }