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