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 }