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 }