github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/builtin/providers/aws/resource_aws_efs_mount_target.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/ec2" 11 "github.com/aws/aws-sdk-go/service/efs" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceAwsEfsMountTarget() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceAwsEfsMountTargetCreate, 19 Read: resourceAwsEfsMountTargetRead, 20 Update: resourceAwsEfsMountTargetUpdate, 21 Delete: resourceAwsEfsMountTargetDelete, 22 23 Importer: &schema.ResourceImporter{ 24 State: schema.ImportStatePassthrough, 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "file_system_id": &schema.Schema{ 29 Type: schema.TypeString, 30 Required: true, 31 ForceNew: true, 32 }, 33 34 "ip_address": &schema.Schema{ 35 Type: schema.TypeString, 36 Computed: true, 37 Optional: true, 38 ForceNew: true, 39 }, 40 41 "security_groups": &schema.Schema{ 42 Type: schema.TypeSet, 43 Elem: &schema.Schema{Type: schema.TypeString}, 44 Set: schema.HashString, 45 Computed: true, 46 Optional: true, 47 }, 48 49 "subnet_id": &schema.Schema{ 50 Type: schema.TypeString, 51 Required: true, 52 ForceNew: true, 53 }, 54 55 "network_interface_id": &schema.Schema{ 56 Type: schema.TypeString, 57 Computed: true, 58 }, 59 "dns_name": &schema.Schema{ 60 Type: schema.TypeString, 61 Computed: true, 62 }, 63 }, 64 } 65 } 66 67 func resourceAwsEfsMountTargetCreate(d *schema.ResourceData, meta interface{}) error { 68 conn := meta.(*AWSClient).efsconn 69 70 fsId := d.Get("file_system_id").(string) 71 subnetId := d.Get("subnet_id").(string) 72 73 // CreateMountTarget would return the same Mount Target ID 74 // to parallel requests if they both include the same AZ 75 // and we would end up managing the same MT as 2 resources. 76 // So we make it fail by calling 1 request per AZ at a time. 77 az, err := getAzFromSubnetId(subnetId, meta.(*AWSClient).ec2conn) 78 if err != nil { 79 return fmt.Errorf("Failed getting AZ from subnet ID (%s): %s", subnetId, err) 80 } 81 mtKey := "efs-mt-" + fsId + "-" + az 82 awsMutexKV.Lock(mtKey) 83 defer awsMutexKV.Unlock(mtKey) 84 85 input := efs.CreateMountTargetInput{ 86 FileSystemId: aws.String(fsId), 87 SubnetId: aws.String(subnetId), 88 } 89 90 if v, ok := d.GetOk("ip_address"); ok { 91 input.IpAddress = aws.String(v.(string)) 92 } 93 if v, ok := d.GetOk("security_groups"); ok { 94 input.SecurityGroups = expandStringList(v.(*schema.Set).List()) 95 } 96 97 log.Printf("[DEBUG] Creating EFS mount target: %#v", input) 98 99 mt, err := conn.CreateMountTarget(&input) 100 if err != nil { 101 return err 102 } 103 104 d.SetId(*mt.MountTargetId) 105 106 stateConf := &resource.StateChangeConf{ 107 Pending: []string{"creating"}, 108 Target: []string{"available"}, 109 Refresh: func() (interface{}, string, error) { 110 resp, err := conn.DescribeMountTargets(&efs.DescribeMountTargetsInput{ 111 MountTargetId: aws.String(d.Id()), 112 }) 113 if err != nil { 114 return nil, "error", err 115 } 116 117 if len(resp.MountTargets) < 1 { 118 return nil, "error", fmt.Errorf("EFS mount target %q not found", d.Id()) 119 } 120 121 mt := resp.MountTargets[0] 122 123 log.Printf("[DEBUG] Current status of %q: %q", *mt.MountTargetId, *mt.LifeCycleState) 124 return mt, *mt.LifeCycleState, nil 125 }, 126 Timeout: 10 * time.Minute, 127 Delay: 2 * time.Second, 128 MinTimeout: 3 * time.Second, 129 } 130 131 _, err = stateConf.WaitForState() 132 if err != nil { 133 return fmt.Errorf("Error waiting for EFS mount target (%s) to create: %s", d.Id(), err) 134 } 135 136 log.Printf("[DEBUG] EFS mount target created: %s", *mt.MountTargetId) 137 138 return resourceAwsEfsMountTargetRead(d, meta) 139 } 140 141 func resourceAwsEfsMountTargetUpdate(d *schema.ResourceData, meta interface{}) error { 142 conn := meta.(*AWSClient).efsconn 143 144 if d.HasChange("security_groups") { 145 input := efs.ModifyMountTargetSecurityGroupsInput{ 146 MountTargetId: aws.String(d.Id()), 147 SecurityGroups: expandStringList(d.Get("security_groups").(*schema.Set).List()), 148 } 149 _, err := conn.ModifyMountTargetSecurityGroups(&input) 150 if err != nil { 151 return err 152 } 153 } 154 155 return resourceAwsEfsMountTargetRead(d, meta) 156 } 157 158 func resourceAwsEfsMountTargetRead(d *schema.ResourceData, meta interface{}) error { 159 conn := meta.(*AWSClient).efsconn 160 resp, err := conn.DescribeMountTargets(&efs.DescribeMountTargetsInput{ 161 MountTargetId: aws.String(d.Id()), 162 }) 163 if err != nil { 164 return err 165 } 166 167 if len(resp.MountTargets) < 1 { 168 return fmt.Errorf("EFS mount target %q not found", d.Id()) 169 } 170 171 mt := resp.MountTargets[0] 172 173 log.Printf("[DEBUG] Found EFS mount target: %#v", mt) 174 175 d.SetId(*mt.MountTargetId) 176 d.Set("file_system_id", *mt.FileSystemId) 177 d.Set("ip_address", *mt.IpAddress) 178 d.Set("subnet_id", *mt.SubnetId) 179 d.Set("network_interface_id", *mt.NetworkInterfaceId) 180 181 sgResp, err := conn.DescribeMountTargetSecurityGroups(&efs.DescribeMountTargetSecurityGroupsInput{ 182 MountTargetId: aws.String(d.Id()), 183 }) 184 if err != nil { 185 return err 186 } 187 188 d.Set("security_groups", schema.NewSet(schema.HashString, flattenStringList(sgResp.SecurityGroups))) 189 190 // DNS name per http://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html 191 az, err := getAzFromSubnetId(*mt.SubnetId, meta.(*AWSClient).ec2conn) 192 if err != nil { 193 return fmt.Errorf("Failed getting AZ from subnet ID (%s): %s", *mt.SubnetId, err) 194 } 195 region := meta.(*AWSClient).region 196 d.Set("dns_name", fmt.Sprintf("%s.%s.efs.%s.amazonaws.com", az, *mt.FileSystemId, region)) 197 198 return nil 199 } 200 201 func getAzFromSubnetId(subnetId string, conn *ec2.EC2) (string, error) { 202 input := ec2.DescribeSubnetsInput{ 203 SubnetIds: []*string{aws.String(subnetId)}, 204 } 205 out, err := conn.DescribeSubnets(&input) 206 if err != nil { 207 return "", err 208 } 209 210 if len(out.Subnets) != 1 { 211 return "", fmt.Errorf("Expected exactly 1 subnet returned for %q", subnetId) 212 } 213 214 return *out.Subnets[0].AvailabilityZone, nil 215 } 216 217 func resourceAwsEfsMountTargetDelete(d *schema.ResourceData, meta interface{}) error { 218 conn := meta.(*AWSClient).efsconn 219 220 log.Printf("[DEBUG] Deleting EFS mount target %q", d.Id()) 221 _, err := conn.DeleteMountTarget(&efs.DeleteMountTargetInput{ 222 MountTargetId: aws.String(d.Id()), 223 }) 224 if err != nil { 225 return err 226 } 227 228 stateConf := &resource.StateChangeConf{ 229 Pending: []string{"available", "deleting", "deleted"}, 230 Target: []string{}, 231 Refresh: func() (interface{}, string, error) { 232 resp, err := conn.DescribeMountTargets(&efs.DescribeMountTargetsInput{ 233 MountTargetId: aws.String(d.Id()), 234 }) 235 if err != nil { 236 awsErr, ok := err.(awserr.Error) 237 if !ok { 238 return nil, "error", err 239 } 240 241 if awsErr.Code() == "MountTargetNotFound" { 242 return nil, "", nil 243 } 244 245 return nil, "error", awsErr 246 } 247 248 if len(resp.MountTargets) < 1 { 249 return nil, "", nil 250 } 251 252 mt := resp.MountTargets[0] 253 254 log.Printf("[DEBUG] Current status of %q: %q", *mt.MountTargetId, *mt.LifeCycleState) 255 return mt, *mt.LifeCycleState, nil 256 }, 257 Timeout: 10 * time.Minute, 258 Delay: 2 * time.Second, 259 MinTimeout: 3 * time.Second, 260 } 261 262 _, err = stateConf.WaitForState() 263 if err != nil { 264 return fmt.Errorf("Error waiting for EFS mount target (%q) to delete: %q", 265 d.Id(), err.Error()) 266 } 267 268 log.Printf("[DEBUG] EFS mount target %q deleted.", d.Id()) 269 270 return nil 271 }