terraform/vendor/github.com/terraform-providers/terraform-provider-openstack/openstack/util.go

278 lines
7.2 KiB
Go

package openstack
import (
"fmt"
"net/http"
"reflect"
"sort"
"strconv"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/gophercloud/gophercloud"
"github.com/hashicorp/terraform/flatmap"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
// BuildRequest takes an opts struct and builds a request body for
// Gophercloud to execute
func BuildRequest(opts interface{}, parent string) (map[string]interface{}, error) {
b, err := gophercloud.BuildRequestBody(opts, "")
if err != nil {
return nil, err
}
b = AddValueSpecs(b)
return map[string]interface{}{parent: b}, nil
}
// CheckDeleted checks the error to see if it's a 404 (Not Found) and, if so,
// sets the resource ID to the empty string instead of throwing an error.
func CheckDeleted(d *schema.ResourceData, err error, msg string) error {
if _, ok := err.(gophercloud.ErrDefault404); ok {
d.SetId("")
return nil
}
return fmt.Errorf("%s %s: %s", msg, d.Id(), err)
}
// GetRegion returns the region that was specified in the resource. If a
// region was not set, the provider-level region is checked. The provider-level
// region can either be set by the region argument or by OS_REGION_NAME.
func GetRegion(d *schema.ResourceData, config *Config) string {
if v, ok := d.GetOk("region"); ok {
return v.(string)
}
return config.Region
}
// AddValueSpecs expands the 'value_specs' object and removes 'value_specs'
// from the reqeust body.
func AddValueSpecs(body map[string]interface{}) map[string]interface{} {
if body["value_specs"] != nil {
for k, v := range body["value_specs"].(map[string]interface{}) {
body[k] = v
}
delete(body, "value_specs")
}
return body
}
// MapValueSpecs converts ResourceData into a map
func MapValueSpecs(d *schema.ResourceData) map[string]string {
m := make(map[string]string)
for key, val := range d.Get("value_specs").(map[string]interface{}) {
m[key] = val.(string)
}
return m
}
// List of headers that need to be redacted
var REDACT_HEADERS = []string{"x-auth-token", "x-auth-key", "x-service-token",
"x-storage-token", "x-account-meta-temp-url-key", "x-account-meta-temp-url-key-2",
"x-container-meta-temp-url-key", "x-container-meta-temp-url-key-2", "set-cookie",
"x-subject-token"}
// RedactHeaders processes a headers object, returning a redacted list
func RedactHeaders(headers http.Header) (processedHeaders []string) {
for name, header := range headers {
for _, v := range header {
if com.IsSliceContainsStr(REDACT_HEADERS, name) {
processedHeaders = append(processedHeaders, fmt.Sprintf("%v: %v", name, "***"))
} else {
processedHeaders = append(processedHeaders, fmt.Sprintf("%v: %v", name, v))
}
}
}
return
}
// FormatHeaders processes a headers object plus a deliminator, returning a string
func FormatHeaders(headers http.Header, seperator string) string {
redactedHeaders := RedactHeaders(headers)
sort.Strings(redactedHeaders)
return strings.Join(redactedHeaders, seperator)
}
func checkForRetryableError(err error) *resource.RetryError {
switch errCode := err.(type) {
case gophercloud.ErrDefault500:
return resource.RetryableError(err)
case gophercloud.ErrUnexpectedResponseCode:
switch errCode.Actual {
case 409, 503:
return resource.RetryableError(err)
default:
return resource.NonRetryableError(err)
}
default:
return resource.NonRetryableError(err)
}
}
func suppressEquivilentTimeDiffs(k, old, new string, d *schema.ResourceData) bool {
oldTime, err := time.Parse(time.RFC3339, old)
if err != nil {
return false
}
newTime, err := time.Parse(time.RFC3339, new)
if err != nil {
return false
}
return oldTime.Equal(newTime)
}
func validateSubnetV2IPv6Mode(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value != "slaac" && value != "dhcpv6-stateful" && value != "dhcpv6-stateless" {
err := fmt.Errorf("%s must be one of slaac, dhcpv6-stateful or dhcpv6-stateless", k)
errors = append(errors, err)
}
return
}
func resourceNetworkingAvailabilityZoneHintsV2(d *schema.ResourceData) []string {
rawAZH := d.Get("availability_zone_hints").([]interface{})
azh := make([]string, len(rawAZH))
for i, raw := range rawAZH {
azh[i] = raw.(string)
}
return azh
}
func expandVendorOptions(vendOptsRaw []interface{}) map[string]interface{} {
vendorOptions := make(map[string]interface{})
for _, option := range vendOptsRaw {
for optKey, optValue := range option.(map[string]interface{}) {
vendorOptions[optKey] = optValue
}
}
return vendorOptions
}
func networkV2ReadAttributesTags(d *schema.ResourceData, tags []string) {
d.Set("all_tags", tags)
allTags := d.Get("all_tags").(*schema.Set)
desiredTags := d.Get("tags").(*schema.Set)
actualTags := allTags.Intersection(desiredTags)
if !actualTags.Equal(desiredTags) {
d.Set("tags", expandToStringSlice(actualTags.List()))
}
}
func networkV2UpdateAttributesTags(d *schema.ResourceData) (tags []string) {
allTags := d.Get("all_tags").(*schema.Set)
oldTagsRaw, newTagsRaw := d.GetChange("tags")
oldTags, newTags := oldTagsRaw.(*schema.Set), newTagsRaw.(*schema.Set)
allWithoutOld := allTags.Difference(oldTags)
return expandToStringSlice(allWithoutOld.Union(newTags).List())
}
func networkV2AttributesTags(d *schema.ResourceData) (tags []string) {
rawTags := d.Get("tags").(*schema.Set).List()
tags = make([]string, len(rawTags))
for i, raw := range rawTags {
tags[i] = raw.(string)
}
return
}
func testAccCheckNetworkingV2Tags(name string, tags []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("resource not found: %s", name)
}
var tagLen int64
var err error
if count, ok := rs.Primary.Attributes["tags.#"]; !ok {
return fmt.Errorf("resource tags not found: %s.tags", name)
} else {
tagLen, err = strconv.ParseInt(count, 10, 64)
if err != nil {
return fmt.Errorf("Failed to parse tag amount: %s", err)
}
}
rtags := make([]string, tagLen)
itags := flatmap.Expand(rs.Primary.Attributes, "tags").([]interface{})
for i, val := range itags {
rtags[i] = val.(string)
}
sort.Strings(rtags)
sort.Strings(tags)
if !reflect.DeepEqual(rtags, tags) {
return fmt.Errorf(
"%s.tags: expected: %#v, got %#v", name, tags, rtags)
}
return nil
}
}
func expandToMapStringString(v map[string]interface{}) map[string]string {
m := make(map[string]string)
for key, val := range v {
if strVal, ok := val.(string); ok {
m[key] = strVal
}
}
return m
}
func expandToStringSlice(v []interface{}) []string {
s := make([]string, len(v))
for i, val := range v {
if strVal, ok := val.(string); ok {
s[i] = strVal
}
}
return s
}
// strSliceContains checks if a given string is contained in a slice
// When anybody asks why Go needs generics, here you go.
func strSliceContains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
func sliceUnion(a, b []string) []string {
var res []string
for _, i := range a {
if !strSliceContains(res, i) {
res = append(res, i)
}
}
for _, k := range b {
if !strSliceContains(res, k) {
res = append(res, k)
}
}
return res
}