github.com/joshgarnett/terraform@v0.5.4-0.20160219181435-92dc20bb3594/builtin/providers/aws/resource_aws_lambda_function.go (about)

     1  package aws
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/aws/awserr"
    12  	"github.com/aws/aws-sdk-go/service/lambda"
    13  	"github.com/mitchellh/go-homedir"
    14  
    15  	"errors"
    16  
    17  	"github.com/hashicorp/terraform/helper/resource"
    18  	"github.com/hashicorp/terraform/helper/schema"
    19  )
    20  
    21  func resourceAwsLambdaFunction() *schema.Resource {
    22  	return &schema.Resource{
    23  		Create: resourceAwsLambdaFunctionCreate,
    24  		Read:   resourceAwsLambdaFunctionRead,
    25  		Update: resourceAwsLambdaFunctionUpdate,
    26  		Delete: resourceAwsLambdaFunctionDelete,
    27  
    28  		Schema: map[string]*schema.Schema{
    29  			"filename": &schema.Schema{
    30  				Type:          schema.TypeString,
    31  				Optional:      true,
    32  				ConflictsWith: []string{"s3_bucket", "s3_key", "s3_object_version"},
    33  			},
    34  			"s3_bucket": &schema.Schema{
    35  				Type:          schema.TypeString,
    36  				Optional:      true,
    37  				ConflictsWith: []string{"filename"},
    38  			},
    39  			"s3_key": &schema.Schema{
    40  				Type:          schema.TypeString,
    41  				Optional:      true,
    42  				ConflictsWith: []string{"filename"},
    43  			},
    44  			"s3_object_version": &schema.Schema{
    45  				Type:          schema.TypeString,
    46  				Optional:      true,
    47  				ConflictsWith: []string{"filename"},
    48  			},
    49  			"description": &schema.Schema{
    50  				Type:     schema.TypeString,
    51  				Optional: true,
    52  			},
    53  			"function_name": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Required: true,
    56  				ForceNew: true,
    57  			},
    58  			"handler": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Required: true,
    61  				ForceNew: true, // TODO make this editable
    62  			},
    63  			"memory_size": &schema.Schema{
    64  				Type:     schema.TypeInt,
    65  				Optional: true,
    66  				Default:  128,
    67  				ForceNew: true, // TODO make this editable
    68  			},
    69  			"role": &schema.Schema{
    70  				Type:     schema.TypeString,
    71  				Required: true,
    72  				ForceNew: true, // TODO make this editable
    73  			},
    74  			"runtime": &schema.Schema{
    75  				Type:     schema.TypeString,
    76  				Optional: true,
    77  				ForceNew: true,
    78  				Default:  "nodejs",
    79  			},
    80  			"timeout": &schema.Schema{
    81  				Type:     schema.TypeInt,
    82  				Optional: true,
    83  				Default:  3,
    84  				ForceNew: true, // TODO make this editable
    85  			},
    86  			"vpc_config": &schema.Schema{
    87  				Type:     schema.TypeList,
    88  				Optional: true,
    89  				ForceNew: true,
    90  				Elem: &schema.Resource{
    91  					Schema: map[string]*schema.Schema{
    92  						"subnet_ids": &schema.Schema{
    93  							Type:     schema.TypeSet,
    94  							Required: true,
    95  							ForceNew: true,
    96  							Elem:     &schema.Schema{Type: schema.TypeString},
    97  							Set:      schema.HashString,
    98  						},
    99  						"security_group_ids": &schema.Schema{
   100  							Type:     schema.TypeSet,
   101  							Required: true,
   102  							ForceNew: true,
   103  							Elem:     &schema.Schema{Type: schema.TypeString},
   104  							Set:      schema.HashString,
   105  						},
   106  					},
   107  				},
   108  			},
   109  			"arn": &schema.Schema{
   110  				Type:     schema.TypeString,
   111  				Computed: true,
   112  			},
   113  			"last_modified": &schema.Schema{
   114  				Type:     schema.TypeString,
   115  				Computed: true,
   116  			},
   117  			"source_code_hash": &schema.Schema{
   118  				Type:     schema.TypeString,
   119  				Computed: true,
   120  				ForceNew: true,
   121  			},
   122  		},
   123  	}
   124  }
   125  
   126  // resourceAwsLambdaFunction maps to:
   127  // CreateFunction in the API / SDK
   128  func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) error {
   129  	conn := meta.(*AWSClient).lambdaconn
   130  
   131  	functionName := d.Get("function_name").(string)
   132  	iamRole := d.Get("role").(string)
   133  
   134  	log.Printf("[DEBUG] Creating Lambda Function %s with role %s", functionName, iamRole)
   135  
   136  	var functionCode *lambda.FunctionCode
   137  	if v, ok := d.GetOk("filename"); ok {
   138  		filename, err := homedir.Expand(v.(string))
   139  		if err != nil {
   140  			return err
   141  		}
   142  		zipfile, err := ioutil.ReadFile(filename)
   143  		if err != nil {
   144  			return err
   145  		}
   146  		d.Set("source_code_hash", sha256.Sum256(zipfile))
   147  		functionCode = &lambda.FunctionCode{
   148  			ZipFile: zipfile,
   149  		}
   150  	} else {
   151  		s3Bucket, bucketOk := d.GetOk("s3_bucket")
   152  		s3Key, keyOk := d.GetOk("s3_key")
   153  		s3ObjectVersion, versionOk := d.GetOk("s3_object_version")
   154  		if !bucketOk || !keyOk || !versionOk {
   155  			return errors.New("s3_bucket, s3_key and s3_object_version must all be set while using S3 code source")
   156  		}
   157  		functionCode = &lambda.FunctionCode{
   158  			S3Bucket:        aws.String(s3Bucket.(string)),
   159  			S3Key:           aws.String(s3Key.(string)),
   160  			S3ObjectVersion: aws.String(s3ObjectVersion.(string)),
   161  		}
   162  	}
   163  
   164  	params := &lambda.CreateFunctionInput{
   165  		Code:         functionCode,
   166  		Description:  aws.String(d.Get("description").(string)),
   167  		FunctionName: aws.String(functionName),
   168  		Handler:      aws.String(d.Get("handler").(string)),
   169  		MemorySize:   aws.Int64(int64(d.Get("memory_size").(int))),
   170  		Role:         aws.String(iamRole),
   171  		Runtime:      aws.String(d.Get("runtime").(string)),
   172  		Timeout:      aws.Int64(int64(d.Get("timeout").(int))),
   173  	}
   174  
   175  	if v, ok := d.GetOk("vpc_config"); ok {
   176  		configs := v.([]interface{})
   177  
   178  		if len(configs) > 1 {
   179  			return errors.New("Only a single vpc_config block is expected")
   180  		} else if len(configs) == 1 {
   181  			config := configs[0].(map[string]interface{})
   182  			var subnetIds []*string
   183  			for _, id := range config["subnet_ids"].(*schema.Set).List() {
   184  				subnetIds = append(subnetIds, aws.String(id.(string)))
   185  			}
   186  
   187  			var securityGroupIds []*string
   188  			for _, id := range config["security_group_ids"].(*schema.Set).List() {
   189  				securityGroupIds = append(securityGroupIds, aws.String(id.(string)))
   190  			}
   191  
   192  			var vpcConfig = &lambda.VpcConfig{
   193  				SubnetIds:        subnetIds,
   194  				SecurityGroupIds: securityGroupIds,
   195  			}
   196  			params.VpcConfig = vpcConfig
   197  		}
   198  	}
   199  
   200  	// IAM profiles can take ~10 seconds to propagate in AWS:
   201  	//  http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
   202  	// Error creating Lambda function: InvalidParameterValueException: The role defined for the task cannot be assumed by Lambda.
   203  	err := resource.Retry(1*time.Minute, func() error {
   204  		_, err := conn.CreateFunction(params)
   205  		if err != nil {
   206  			if awserr, ok := err.(awserr.Error); ok {
   207  				if awserr.Code() == "InvalidParameterValueException" {
   208  					log.Printf("[DEBUG] InvalidParameterValueException creating Lambda Function: %s", awserr)
   209  					// Retryable
   210  					return awserr
   211  				}
   212  			}
   213  			log.Printf("[DEBUG] Error creating Lambda Function: %s", err)
   214  			// Not retryable
   215  			return resource.RetryError{Err: err}
   216  		}
   217  		// No error
   218  		return nil
   219  	})
   220  	if err != nil {
   221  		return fmt.Errorf("Error creating Lambda function: %s", err)
   222  	}
   223  
   224  	d.SetId(d.Get("function_name").(string))
   225  
   226  	return resourceAwsLambdaFunctionRead(d, meta)
   227  }
   228  
   229  // resourceAwsLambdaFunctionRead maps to:
   230  // GetFunction in the API / SDK
   231  func resourceAwsLambdaFunctionRead(d *schema.ResourceData, meta interface{}) error {
   232  	conn := meta.(*AWSClient).lambdaconn
   233  
   234  	log.Printf("[DEBUG] Fetching Lambda Function: %s", d.Id())
   235  
   236  	params := &lambda.GetFunctionInput{
   237  		FunctionName: aws.String(d.Get("function_name").(string)),
   238  	}
   239  
   240  	getFunctionOutput, err := conn.GetFunction(params)
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	// getFunctionOutput.Code.Location is a pre-signed URL pointing at the zip
   246  	// file that we uploaded when we created the resource. You can use it to
   247  	// download the code from AWS. The other part is
   248  	// getFunctionOutput.Configuration which holds metadata.
   249  
   250  	function := getFunctionOutput.Configuration
   251  	// TODO error checking / handling on the Set() calls.
   252  	d.Set("arn", function.FunctionArn)
   253  	d.Set("description", function.Description)
   254  	d.Set("handler", function.Handler)
   255  	d.Set("memory_size", function.MemorySize)
   256  	d.Set("last_modified", function.LastModified)
   257  	d.Set("role", function.Role)
   258  	d.Set("runtime", function.Runtime)
   259  	d.Set("timeout", function.Timeout)
   260  	d.Set("vpc_config", flattenLambdaVpcConfigResponse(function.VpcConfig))
   261  
   262  	return nil
   263  }
   264  
   265  // resourceAwsLambdaFunction maps to:
   266  // DeleteFunction in the API / SDK
   267  func resourceAwsLambdaFunctionDelete(d *schema.ResourceData, meta interface{}) error {
   268  	conn := meta.(*AWSClient).lambdaconn
   269  
   270  	log.Printf("[INFO] Deleting Lambda Function: %s", d.Id())
   271  
   272  	params := &lambda.DeleteFunctionInput{
   273  		FunctionName: aws.String(d.Get("function_name").(string)),
   274  	}
   275  
   276  	_, err := conn.DeleteFunction(params)
   277  	if err != nil {
   278  		return fmt.Errorf("Error deleting Lambda Function: %s", err)
   279  	}
   280  
   281  	d.SetId("")
   282  
   283  	return nil
   284  }
   285  
   286  // resourceAwsLambdaFunctionUpdate maps to:
   287  // UpdateFunctionCode in the API / SDK
   288  func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) error {
   289  	// conn := meta.(*AWSClient).lambdaconn
   290  
   291  	return nil
   292  }