github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_glacier_vault.go (about)

     1  package aws
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"regexp"
     8  
     9  	"github.com/hashicorp/errwrap"
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  
    12  	"github.com/aws/aws-sdk-go/aws"
    13  	"github.com/aws/aws-sdk-go/aws/awserr"
    14  	"github.com/aws/aws-sdk-go/service/glacier"
    15  )
    16  
    17  func resourceAwsGlacierVault() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsGlacierVaultCreate,
    20  		Read:   resourceAwsGlacierVaultRead,
    21  		Update: resourceAwsGlacierVaultUpdate,
    22  		Delete: resourceAwsGlacierVaultDelete,
    23  
    24  		Importer: &schema.ResourceImporter{
    25  			State: schema.ImportStatePassthrough,
    26  		},
    27  
    28  		Schema: map[string]*schema.Schema{
    29  			"name": &schema.Schema{
    30  				Type:     schema.TypeString,
    31  				Required: true,
    32  				ForceNew: true,
    33  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    34  					value := v.(string)
    35  					if !regexp.MustCompile(`^[.0-9A-Za-z-_]+$`).MatchString(value) {
    36  						errors = append(errors, fmt.Errorf(
    37  							"only alphanumeric characters, hyphens, underscores, and periods are allowed in %q", k))
    38  					}
    39  					if len(value) > 255 {
    40  						errors = append(errors, fmt.Errorf(
    41  							"%q cannot be longer than 255 characters", k))
    42  					}
    43  					return
    44  				},
    45  			},
    46  
    47  			"location": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Computed: true,
    50  			},
    51  
    52  			"arn": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Computed: true,
    55  			},
    56  
    57  			"access_policy": &schema.Schema{
    58  				Type:         schema.TypeString,
    59  				Optional:     true,
    60  				ValidateFunc: validateJsonString,
    61  				StateFunc: func(v interface{}) string {
    62  					json, _ := normalizeJsonString(v)
    63  					return json
    64  				},
    65  			},
    66  
    67  			"notification": &schema.Schema{
    68  				Type:     schema.TypeList,
    69  				Optional: true,
    70  				Elem: &schema.Resource{
    71  					Schema: map[string]*schema.Schema{
    72  						"events": &schema.Schema{
    73  							Type:     schema.TypeSet,
    74  							Required: true,
    75  							Elem:     &schema.Schema{Type: schema.TypeString},
    76  							Set:      schema.HashString,
    77  						},
    78  						"sns_topic": &schema.Schema{
    79  							Type:     schema.TypeString,
    80  							Required: true,
    81  						},
    82  					},
    83  				},
    84  			},
    85  
    86  			"tags": tagsSchema(),
    87  		},
    88  	}
    89  }
    90  
    91  func resourceAwsGlacierVaultCreate(d *schema.ResourceData, meta interface{}) error {
    92  	glacierconn := meta.(*AWSClient).glacierconn
    93  
    94  	input := &glacier.CreateVaultInput{
    95  		VaultName: aws.String(d.Get("name").(string)),
    96  	}
    97  
    98  	out, err := glacierconn.CreateVault(input)
    99  	if err != nil {
   100  		return fmt.Errorf("Error creating Glacier Vault: %s", err)
   101  	}
   102  
   103  	d.SetId(d.Get("name").(string))
   104  	d.Set("location", *out.Location)
   105  
   106  	return resourceAwsGlacierVaultUpdate(d, meta)
   107  }
   108  
   109  func resourceAwsGlacierVaultUpdate(d *schema.ResourceData, meta interface{}) error {
   110  	glacierconn := meta.(*AWSClient).glacierconn
   111  
   112  	if err := setGlacierVaultTags(glacierconn, d); err != nil {
   113  		return err
   114  	}
   115  
   116  	if d.HasChange("access_policy") {
   117  		if err := resourceAwsGlacierVaultPolicyUpdate(glacierconn, d); err != nil {
   118  			return err
   119  		}
   120  	}
   121  
   122  	if d.HasChange("notification") {
   123  		if err := resourceAwsGlacierVaultNotificationUpdate(glacierconn, d); err != nil {
   124  			return err
   125  		}
   126  	}
   127  
   128  	return resourceAwsGlacierVaultRead(d, meta)
   129  }
   130  
   131  func resourceAwsGlacierVaultRead(d *schema.ResourceData, meta interface{}) error {
   132  	glacierconn := meta.(*AWSClient).glacierconn
   133  
   134  	input := &glacier.DescribeVaultInput{
   135  		VaultName: aws.String(d.Id()),
   136  	}
   137  
   138  	out, err := glacierconn.DescribeVault(input)
   139  	if err != nil {
   140  		return fmt.Errorf("Error reading Glacier Vault: %s", err.Error())
   141  	}
   142  
   143  	awsClient := meta.(*AWSClient)
   144  	d.Set("name", out.VaultName)
   145  	d.Set("arn", out.VaultARN)
   146  
   147  	location, err := buildGlacierVaultLocation(awsClient.accountid, d.Id())
   148  	if err != nil {
   149  		return err
   150  	}
   151  	d.Set("location", location)
   152  
   153  	tags, err := getGlacierVaultTags(glacierconn, d.Id())
   154  	if err != nil {
   155  		return err
   156  	}
   157  	d.Set("tags", tags)
   158  
   159  	log.Printf("[DEBUG] Getting the access_policy for Vault %s", d.Id())
   160  	pol, err := glacierconn.GetVaultAccessPolicy(&glacier.GetVaultAccessPolicyInput{
   161  		VaultName: aws.String(d.Id()),
   162  	})
   163  
   164  	if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" {
   165  		d.Set("access_policy", "")
   166  	} else if pol != nil {
   167  		policy, err := normalizeJsonString(*pol.Policy.Policy)
   168  		if err != nil {
   169  			return errwrap.Wrapf("access policy contains an invalid JSON: {{err}}", err)
   170  		}
   171  		d.Set("access_policy", policy)
   172  	} else {
   173  		return err
   174  	}
   175  
   176  	notifications, err := getGlacierVaultNotification(glacierconn, d.Id())
   177  	if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" {
   178  		d.Set("notification", "")
   179  	} else if pol != nil {
   180  		d.Set("notification", notifications)
   181  	} else {
   182  		return err
   183  	}
   184  
   185  	return nil
   186  }
   187  
   188  func resourceAwsGlacierVaultDelete(d *schema.ResourceData, meta interface{}) error {
   189  	glacierconn := meta.(*AWSClient).glacierconn
   190  
   191  	log.Printf("[DEBUG] Glacier Delete Vault: %s", d.Id())
   192  	_, err := glacierconn.DeleteVault(&glacier.DeleteVaultInput{
   193  		VaultName: aws.String(d.Id()),
   194  	})
   195  	if err != nil {
   196  		return fmt.Errorf("Error deleting Glacier Vault: %s", err.Error())
   197  	}
   198  	return nil
   199  }
   200  
   201  func resourceAwsGlacierVaultNotificationUpdate(glacierconn *glacier.Glacier, d *schema.ResourceData) error {
   202  
   203  	if v, ok := d.GetOk("notification"); ok {
   204  		settings := v.([]interface{})
   205  
   206  		if len(settings) > 1 {
   207  			return fmt.Errorf("Only a single Notification Block is allowed for Glacier Vault")
   208  		} else if len(settings) == 1 {
   209  			s := settings[0].(map[string]interface{})
   210  			var events []*string
   211  			for _, id := range s["events"].(*schema.Set).List() {
   212  				events = append(events, aws.String(id.(string)))
   213  			}
   214  
   215  			_, err := glacierconn.SetVaultNotifications(&glacier.SetVaultNotificationsInput{
   216  				VaultName: aws.String(d.Id()),
   217  				VaultNotificationConfig: &glacier.VaultNotificationConfig{
   218  					SNSTopic: aws.String(s["sns_topic"].(string)),
   219  					Events:   events,
   220  				},
   221  			})
   222  
   223  			if err != nil {
   224  				return fmt.Errorf("Error Updating Glacier Vault Notifications: %s", err.Error())
   225  			}
   226  		}
   227  	} else {
   228  		_, err := glacierconn.DeleteVaultNotifications(&glacier.DeleteVaultNotificationsInput{
   229  			VaultName: aws.String(d.Id()),
   230  		})
   231  
   232  		if err != nil {
   233  			return fmt.Errorf("Error Removing Glacier Vault Notifications: %s", err.Error())
   234  		}
   235  
   236  	}
   237  
   238  	return nil
   239  }
   240  
   241  func resourceAwsGlacierVaultPolicyUpdate(glacierconn *glacier.Glacier, d *schema.ResourceData) error {
   242  	vaultName := d.Id()
   243  	policyContents := d.Get("access_policy").(string)
   244  
   245  	policy := &glacier.VaultAccessPolicy{
   246  		Policy: aws.String(policyContents),
   247  	}
   248  
   249  	if policyContents != "" {
   250  		log.Printf("[DEBUG] Glacier Vault: %s, put policy", vaultName)
   251  
   252  		_, err := glacierconn.SetVaultAccessPolicy(&glacier.SetVaultAccessPolicyInput{
   253  			VaultName: aws.String(d.Id()),
   254  			Policy:    policy,
   255  		})
   256  
   257  		if err != nil {
   258  			return fmt.Errorf("Error putting Glacier Vault policy: %s", err.Error())
   259  		}
   260  	} else {
   261  		log.Printf("[DEBUG] Glacier Vault: %s, delete policy: %s", vaultName, policy)
   262  		_, err := glacierconn.DeleteVaultAccessPolicy(&glacier.DeleteVaultAccessPolicyInput{
   263  			VaultName: aws.String(d.Id()),
   264  		})
   265  
   266  		if err != nil {
   267  			return fmt.Errorf("Error deleting Glacier Vault policy: %s", err.Error())
   268  		}
   269  	}
   270  
   271  	return nil
   272  }
   273  
   274  func setGlacierVaultTags(conn *glacier.Glacier, d *schema.ResourceData) error {
   275  	if d.HasChange("tags") {
   276  		oraw, nraw := d.GetChange("tags")
   277  		o := oraw.(map[string]interface{})
   278  		n := nraw.(map[string]interface{})
   279  		create, remove := diffGlacierVaultTags(mapGlacierVaultTags(o), mapGlacierVaultTags(n))
   280  
   281  		// Set tags
   282  		if len(remove) > 0 {
   283  			tagsToRemove := &glacier.RemoveTagsFromVaultInput{
   284  				VaultName: aws.String(d.Id()),
   285  				TagKeys:   glacierStringsToPointyString(remove),
   286  			}
   287  
   288  			log.Printf("[DEBUG] Removing tags: from %s", d.Id())
   289  			_, err := conn.RemoveTagsFromVault(tagsToRemove)
   290  			if err != nil {
   291  				return err
   292  			}
   293  		}
   294  		if len(create) > 0 {
   295  			tagsToAdd := &glacier.AddTagsToVaultInput{
   296  				VaultName: aws.String(d.Id()),
   297  				Tags:      glacierVaultTagsFromMap(create),
   298  			}
   299  
   300  			log.Printf("[DEBUG] Creating tags: for %s", d.Id())
   301  			_, err := conn.AddTagsToVault(tagsToAdd)
   302  			if err != nil {
   303  				return err
   304  			}
   305  		}
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  func mapGlacierVaultTags(m map[string]interface{}) map[string]string {
   312  	results := make(map[string]string)
   313  	for k, v := range m {
   314  		results[k] = v.(string)
   315  	}
   316  
   317  	return results
   318  }
   319  
   320  func diffGlacierVaultTags(oldTags, newTags map[string]string) (map[string]string, []string) {
   321  
   322  	create := make(map[string]string)
   323  	for k, v := range newTags {
   324  		create[k] = v
   325  	}
   326  
   327  	// Build the list of what to remove
   328  	var remove []string
   329  	for k, v := range oldTags {
   330  		old, ok := create[k]
   331  		if !ok || old != v {
   332  			// Delete it!
   333  			remove = append(remove, k)
   334  		}
   335  	}
   336  
   337  	return create, remove
   338  }
   339  
   340  func getGlacierVaultTags(glacierconn *glacier.Glacier, vaultName string) (map[string]string, error) {
   341  	request := &glacier.ListTagsForVaultInput{
   342  		VaultName: aws.String(vaultName),
   343  	}
   344  
   345  	log.Printf("[DEBUG] Getting the tags: for %s", vaultName)
   346  	response, err := glacierconn.ListTagsForVault(request)
   347  	if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "NoSuchTagSet" {
   348  		return map[string]string{}, nil
   349  	} else if err != nil {
   350  		return nil, err
   351  	}
   352  
   353  	return glacierVaultTagsToMap(response.Tags), nil
   354  }
   355  
   356  func glacierVaultTagsToMap(responseTags map[string]*string) map[string]string {
   357  	results := make(map[string]string, len(responseTags))
   358  	for k, v := range responseTags {
   359  		results[k] = *v
   360  	}
   361  
   362  	return results
   363  }
   364  
   365  func glacierVaultTagsFromMap(responseTags map[string]string) map[string]*string {
   366  	results := make(map[string]*string, len(responseTags))
   367  	for k, v := range responseTags {
   368  		results[k] = aws.String(v)
   369  	}
   370  
   371  	return results
   372  }
   373  
   374  func glacierStringsToPointyString(s []string) []*string {
   375  	results := make([]*string, len(s))
   376  	for i, x := range s {
   377  		results[i] = aws.String(x)
   378  	}
   379  
   380  	return results
   381  }
   382  
   383  func glacierPointersToStringList(pointers []*string) []interface{} {
   384  	list := make([]interface{}, len(pointers))
   385  	for i, v := range pointers {
   386  		list[i] = *v
   387  	}
   388  	return list
   389  }
   390  
   391  func buildGlacierVaultLocation(accountId, vaultName string) (string, error) {
   392  	if accountId == "" {
   393  		return "", errors.New("AWS account ID unavailable - failed to construct Vault location")
   394  	}
   395  	return fmt.Sprintf("/" + accountId + "/vaults/" + vaultName), nil
   396  }
   397  
   398  func getGlacierVaultNotification(glacierconn *glacier.Glacier, vaultName string) ([]map[string]interface{}, error) {
   399  	request := &glacier.GetVaultNotificationsInput{
   400  		VaultName: aws.String(vaultName),
   401  	}
   402  
   403  	response, err := glacierconn.GetVaultNotifications(request)
   404  	if err != nil {
   405  		return nil, fmt.Errorf("Error reading Glacier Vault Notifications: %s", err.Error())
   406  	}
   407  
   408  	notifications := make(map[string]interface{}, 0)
   409  
   410  	log.Print("[DEBUG] Flattening Glacier Vault Notifications")
   411  
   412  	notifications["events"] = schema.NewSet(schema.HashString, glacierPointersToStringList(response.VaultNotificationConfig.Events))
   413  	notifications["sns_topic"] = *response.VaultNotificationConfig.SNSTopic
   414  
   415  	return []map[string]interface{}{notifications}, nil
   416  }