-
Notifications
You must be signed in to change notification settings - Fork 9.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
provider/aws: Elastic Beanstalk Application Version #5770
Changes from all commits
1a89270
29be271
f7cf6af
8030158
807803c
9662918
400e85d
330de1d
6eaeb5c
d006bad
8981fa1
0a8eed0
14a307d
c2b9c0b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
"github.com/aws/aws-sdk-go/service/elasticbeanstalk" | ||
"time" | ||
) | ||
|
||
func resourceAwsElasticBeanstalkApplicationVersion() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsElasticBeanstalkApplicationVersionCreate, | ||
Read: resourceAwsElasticBeanstalkApplicationVersionRead, | ||
Update: resourceAwsElasticBeanstalkApplicationVersionUpdate, | ||
Delete: resourceAwsElasticBeanstalkApplicationVersionDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"application": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"description": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"bucket": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"key": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"force_delete": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: false, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsElasticBeanstalkApplicationVersionCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).elasticbeanstalkconn | ||
|
||
application := d.Get("application").(string) | ||
description := d.Get("description").(string) | ||
bucket := d.Get("bucket").(string) | ||
key := d.Get("key").(string) | ||
name := d.Get("name").(string) | ||
|
||
s3Location := elasticbeanstalk.S3Location{ | ||
S3Bucket: aws.String(bucket), | ||
S3Key: aws.String(key), | ||
} | ||
|
||
createOpts := elasticbeanstalk.CreateApplicationVersionInput{ | ||
ApplicationName: aws.String(application), | ||
Description: aws.String(description), | ||
SourceBundle: &s3Location, | ||
VersionLabel: aws.String(name), | ||
} | ||
|
||
log.Printf("[DEBUG] Elastic Beanstalk Application Version create opts: %s", createOpts) | ||
_, err := conn.CreateApplicationVersion(&createOpts) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(name) | ||
log.Printf("[INFO] Elastic Beanstalk Application Version Label: %s", name) | ||
|
||
return resourceAwsElasticBeanstalkApplicationVersionRead(d, meta) | ||
} | ||
|
||
func resourceAwsElasticBeanstalkApplicationVersionRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).elasticbeanstalkconn | ||
|
||
resp, err := conn.DescribeApplicationVersions(&elasticbeanstalk.DescribeApplicationVersionsInput{ | ||
VersionLabels: []*string{aws.String(d.Id())}, | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
if len(resp.ApplicationVersions) == 0 { | ||
log.Printf("[DEBUG] Elastic Beanstalk application version read: application version not found") | ||
|
||
d.SetId("") | ||
|
||
return nil | ||
} else if len(resp.ApplicationVersions) != 1 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm curious, it's my understanding that users will have a unique If I name one version There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case test-version would be the only application version returned. In this context it should only ever return one version, if not something is very wrong 😄 As for your first question here, it would either be a unique application version per version or per release. In thinking about this question I realized that in its current state this may not work in some cases. I'm going to add a comment describing this a bit more in the pr, as it may take a bit of work to fix. |
||
return fmt.Errorf("Error reading application version properties: found %d application versions, expected 1", len(resp.ApplicationVersions)) | ||
} | ||
|
||
if err := d.Set("description", resp.ApplicationVersions[0].Description); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsElasticBeanstalkApplicationVersionUpdate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).elasticbeanstalkconn | ||
|
||
if d.HasChange("description") { | ||
if err := resourceAwsElasticBeanstalkApplicationVersionDescriptionUpdate(conn, d); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return resourceAwsElasticBeanstalkApplicationVersionRead(d, meta) | ||
|
||
} | ||
|
||
func resourceAwsElasticBeanstalkApplicationVersionDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).elasticbeanstalkconn | ||
|
||
application := d.Get("application").(string) | ||
name := d.Id() | ||
|
||
if d.Get("force_delete").(bool) == false { | ||
environments, err := versionUsedBy(application, name, conn) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if len(environments) > 1 { | ||
return fmt.Errorf("Unable to delete Application Version, it is currently in use by the following environments: %s.", environments) | ||
} | ||
} | ||
_, err := conn.DeleteApplicationVersion(&elasticbeanstalk.DeleteApplicationVersionInput{ | ||
ApplicationName: aws.String(application), | ||
VersionLabel: aws.String(name), | ||
DeleteSourceBundle: aws.Bool(false), | ||
}) | ||
|
||
if err != nil { | ||
if awserr, ok := err.(awserr.Error); ok { | ||
// application version is pending delete, or no longer exists. | ||
if awserr.Code() == "InvalidParameterValue" { | ||
d.SetId("") | ||
return nil | ||
} | ||
} | ||
return err | ||
} | ||
|
||
d.SetId("") | ||
return nil | ||
} | ||
|
||
func resourceAwsElasticBeanstalkApplicationVersionDescriptionUpdate(conn *elasticbeanstalk.ElasticBeanstalk, d *schema.ResourceData) error { | ||
application := d.Get("application").(string) | ||
description := d.Get("description").(string) | ||
name := d.Get("name").(string) | ||
|
||
log.Printf("[DEBUG] Elastic Beanstalk application version: %s, update description: %s", name, description) | ||
|
||
_, err := conn.UpdateApplicationVersion(&elasticbeanstalk.UpdateApplicationVersionInput{ | ||
ApplicationName: aws.String(application), | ||
Description: aws.String(description), | ||
VersionLabel: aws.String(name), | ||
}) | ||
|
||
return err | ||
} | ||
|
||
func versionUsedBy(applicationName, versionLabel string, conn *elasticbeanstalk.ElasticBeanstalk) ([]string, error) { | ||
now := time.Now() | ||
resp, err := conn.DescribeEnvironments(&elasticbeanstalk.DescribeEnvironmentsInput{ | ||
ApplicationName: aws.String(applicationName), | ||
VersionLabel: aws.String(versionLabel), | ||
IncludeDeleted: aws.Bool(true), | ||
IncludedDeletedBackTo: aws.Time(now.Add(-1 * time.Minute)), | ||
}) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var environmentIDs []string | ||
for _, environment := range resp.Environments { | ||
environmentIDs = append(environmentIDs, *environment.EnvironmentId) | ||
} | ||
|
||
return environmentIDs, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
"github.com/aws/aws-sdk-go/service/elasticbeanstalk" | ||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccAWSBeanstalkAppVersion_basic(t *testing.T) { | ||
|
||
var appVersion elasticbeanstalk.ApplicationVersionDescription | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckApplicationVersionDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testAccBeanstalkApplicationVersionConfig(acctest.RandInt()), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckApplicationVersionExists("aws_elastic_beanstalk_application_version.default", &appVersion), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckApplicationVersionDestroy(s *terraform.State) error { | ||
conn := testAccProvider.Meta().(*AWSClient).elasticbeanstalkconn | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "aws_elastic_beanstalk_application_version" { | ||
continue | ||
} | ||
|
||
describeApplicationVersionOpts := &elasticbeanstalk.DescribeApplicationVersionsInput{ | ||
VersionLabels: []*string{aws.String(rs.Primary.ID)}, | ||
} | ||
resp, err := conn.DescribeApplicationVersions(describeApplicationVersionOpts) | ||
if err == nil { | ||
if len(resp.ApplicationVersions) > 0 { | ||
return fmt.Errorf("Elastic Beanstalk Application Verson still exists.") | ||
} | ||
|
||
return nil | ||
} | ||
ec2err, ok := err.(awserr.Error) | ||
if !ok { | ||
return err | ||
} | ||
if ec2err.Code() != "InvalidParameterValue" { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testAccCheckApplicationVersionExists(n string, app *elasticbeanstalk.ApplicationVersionDescription) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", n) | ||
} | ||
|
||
if rs.Primary.ID == "" { | ||
return fmt.Errorf("Elastic Beanstalk Application Version is not set") | ||
} | ||
|
||
conn := testAccProvider.Meta().(*AWSClient).elasticbeanstalkconn | ||
describeApplicationVersionOpts := &elasticbeanstalk.DescribeApplicationVersionsInput{ | ||
VersionLabels: []*string{aws.String(rs.Primary.ID)}, | ||
} | ||
|
||
log.Printf("[DEBUG] Elastic Beanstalk Application Version TEST describe opts: %s", describeApplicationVersionOpts) | ||
|
||
resp, err := conn.DescribeApplicationVersions(describeApplicationVersionOpts) | ||
if err != nil { | ||
return err | ||
} | ||
if len(resp.ApplicationVersions) == 0 { | ||
return fmt.Errorf("Elastic Beanstalk Application Version not found.") | ||
} | ||
|
||
*app = *resp.ApplicationVersions[0] | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccBeanstalkApplicationVersionConfig(randInt int) string { | ||
return fmt.Sprintf(` | ||
resource "aws_s3_bucket" "default" { | ||
bucket = "tftest.applicationversion.bucket-%d" | ||
} | ||
|
||
resource "aws_s3_bucket_object" "default" { | ||
bucket = "${aws_s3_bucket.default.id}" | ||
key = "beanstalk/python-v1.zip" | ||
source = "test-fixtures/python-v1.zip" | ||
} | ||
|
||
resource "aws_elastic_beanstalk_application" "default" { | ||
name = "tf-test-name" | ||
description = "tf-test-desc" | ||
} | ||
|
||
resource "aws_elastic_beanstalk_application_version" "default" { | ||
application = "tf-test-name" | ||
name = "tf-test-version-label" | ||
bucket = "${aws_s3_bucket.default.id}" | ||
key = "${aws_s3_bucket_object.default.id}" | ||
} | ||
`, randInt) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,6 +67,11 @@ func resourceAwsElasticBeanstalkEnvironment() *schema.Resource { | |
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"version_label": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you apply your example above, does this field show up in future diffs? It looks like it would need to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it does, at least when I change the value in my Terraform document, it is aware of the change when running Related to this, I did see a case where the version field wasn't being managed by Terraform. Subsequent plans would show the value changing from the external value to "". To work around that Not sure I completely understand the purpose of |
||
Computed: true, | ||
}, | ||
"cname": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Computed: true, | ||
|
@@ -196,6 +201,7 @@ func resourceAwsElasticBeanstalkEnvironmentCreate(d *schema.ResourceData, meta i | |
tier := d.Get("tier").(string) | ||
app := d.Get("application").(string) | ||
desc := d.Get("description").(string) | ||
version := d.Get("version_label").(string) | ||
settings := d.Get("setting").(*schema.Set) | ||
solutionStack := d.Get("solution_stack_name").(string) | ||
templateName := d.Get("template_name").(string) | ||
|
@@ -245,6 +251,10 @@ func resourceAwsElasticBeanstalkEnvironmentCreate(d *schema.ResourceData, meta i | |
createOpts.TemplateName = aws.String(templateName) | ||
} | ||
|
||
if version != "" { | ||
createOpts.VersionLabel = aws.String(version) | ||
} | ||
|
||
// Get the current time to filter describeBeanstalkEvents messages | ||
t := time.Now() | ||
log.Printf("[DEBUG] Elastic Beanstalk Environment create opts: %s", createOpts) | ||
|
@@ -387,6 +397,11 @@ func resourceAwsElasticBeanstalkEnvironmentUpdate(d *schema.ResourceData, meta i | |
} | ||
} | ||
|
||
if d.HasChange("version_label") { | ||
hasChange = true | ||
updateOpts.VersionLabel = aws.String(d.Get("version_label").(string)) | ||
} | ||
|
||
if hasChange { | ||
// Get the current time to filter describeBeanstalkEvents messages | ||
t := time.Now() | ||
|
@@ -489,6 +504,10 @@ func resourceAwsElasticBeanstalkEnvironmentRead(d *schema.ResourceData, meta int | |
return err | ||
} | ||
|
||
if err := d.Set("version_label", env.VersionLabel); err != nil { | ||
return err | ||
} | ||
|
||
if err := d.Set("tier", *env.Tier.Name); err != nil { | ||
return err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
bucket
is optional, what happens here if the User omits it from the config? I assume a failure in theCreateApplicationVersion
call. We need to conditionally added this, if that is the caseThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to make bucket and key required for the reasons discussed below on the documentation note.