provider/aws: Add Lightsail Instance

Adds initial support for AWS Lightsail Instances
This commit is contained in:
clint shryock 2016-12-01 11:35:11 -06:00
parent 08a56304bb
commit 44ffe71695
9 changed files with 9832 additions and 0 deletions

View File

@ -45,6 +45,7 @@ import (
"github.com/aws/aws-sdk-go/service/kinesis"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/service/lambda"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/aws/aws-sdk-go/service/opsworks"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/redshift"
@ -135,6 +136,7 @@ type AWSClient struct {
elasticbeanstalkconn *elasticbeanstalk.ElasticBeanstalk
elastictranscoderconn *elastictranscoder.ElasticTranscoder
lambdaconn *lambda.Lambda
lightsailconn *lightsail.Lightsail
opsworksconn *opsworks.OpsWorks
glacierconn *glacier.Glacier
codedeployconn *codedeploy.CodeDeploy
@ -282,6 +284,7 @@ func (c *Config) Client() (interface{}, error) {
client.kinesisconn = kinesis.New(kinesisSess)
client.kmsconn = kms.New(sess)
client.lambdaconn = lambda.New(sess)
client.lightsailconn = lightsail.New(usEast1Sess)
client.opsworksconn = opsworks.New(usEast1Sess)
client.r53conn = route53.New(usEast1Sess)
client.rdsconn = rds.New(sess)

View File

@ -284,6 +284,7 @@ func Provider() terraform.ResourceProvider {
"aws_lambda_alias": resourceAwsLambdaAlias(),
"aws_lambda_permission": resourceAwsLambdaPermission(),
"aws_launch_configuration": resourceAwsLaunchConfiguration(),
"aws_lightsail_instance": resourceAwsLightsailInstance(),
"aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(),
"aws_load_balancer_policy": resourceAwsLoadBalancerPolicy(),
"aws_load_balancer_backend_server_policy": resourceAwsLoadBalancerBackendServerPolicies(),

View File

@ -0,0 +1,264 @@
package aws
import (
"fmt"
"log"
"strconv"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsLightsailInstance() *schema.Resource {
return &schema.Resource{
Create: resourceAwsLightsailInstanceCreate,
Read: resourceAwsLightsailInstanceRead,
Delete: resourceAwsLightsailInstanceDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"availability_zone": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"blueprint_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"bundle_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
// Optional attributes
"key_pair_name": {
// Not compatible with aws_key_pair (yet)
// We'll need a new aws_lightsail_key_pair resource
Type: schema.TypeString,
Optional: true,
ForceNew: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if old == "LightsailDefaultKeyPair" && new == "" {
return true
}
return false
},
},
// cannot be retrieved from the API
"user_data": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
// additional info returned from the API
"arn": {
Type: schema.TypeString,
Computed: true,
},
"created_at": {
Type: schema.TypeString,
Computed: true,
},
"cpu_count": {
Type: schema.TypeInt,
Computed: true,
},
"ram_size": {
Type: schema.TypeInt,
Computed: true,
},
"ipv6_address": {
Type: schema.TypeString,
Computed: true,
},
"is_static_ip": {
Type: schema.TypeBool,
Computed: true,
},
"private_ip_address": {
Type: schema.TypeString,
Computed: true,
},
"public_ip_address": {
Type: schema.TypeString,
Computed: true,
},
"username": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceAwsLightsailInstanceCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
iName := d.Get("name").(string)
req := lightsail.CreateInstancesInput{
AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
BlueprintId: aws.String(d.Get("blueprint_id").(string)),
BundleId: aws.String(d.Get("bundle_id").(string)),
InstanceNames: aws.StringSlice([]string{iName}),
}
if v, ok := d.GetOk("key_pair_name"); ok {
req.KeyPairName = aws.String(v.(string))
}
if v, ok := d.GetOk("user_data"); ok {
req.UserData = aws.String(v.(string))
}
resp, err := conn.CreateInstances(&req)
if err != nil {
return err
}
if len(resp.Operations) == 0 {
return fmt.Errorf("[ERR] No operations found for CreateInstance request")
}
op := resp.Operations[0]
d.SetId(d.Get("name").(string))
stateConf := &resource.StateChangeConf{
Pending: []string{"Started"},
Target: []string{"Completed", "Succeeded"},
Refresh: resourceAwsLightsailInstanceOperationRefreshFunc(op.Id, meta),
Timeout: 10 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
// We don't return an error here because the Create call succeded
log.Printf("[ERR] Error waiting for instance (%s) to become ready: %s", d.Id(), err)
}
return resourceAwsLightsailInstanceRead(d, meta)
}
func resourceAwsLightsailInstanceRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
resp, err := conn.GetInstance(&lightsail.GetInstanceInput{
InstanceName: aws.String(d.Id()),
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NotFoundException" {
log.Printf("[WARN] Lightsail Instance (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
return err
}
return err
}
if resp == nil {
log.Printf("[WARN] Lightsail Instance (%s) not found, nil response from server, removing from state", d.Id())
d.SetId("")
return nil
}
i := resp.Instance
d.Set("availability_zone", i.Location.AvailabilityZone)
d.Set("blueprint_id", i.BlueprintId)
d.Set("bundle_id", i.BundleId)
d.Set("key_pair_name", i.SshKeyName)
d.Set("name", i.Name)
// additional attributes
d.Set("arn", i.Arn)
d.Set("username", i.Username)
d.Set("created_at", i.CreatedAt.Format(time.RFC3339))
d.Set("cpu_count", i.Hardware.CpuCount)
d.Set("ram_size", strconv.FormatFloat(*i.Hardware.RamSizeInGb, 'f', 0, 64))
d.Set("ipv6_address", i.Ipv6Address)
d.Set("is_static_ip", i.IsStaticIp)
d.Set("private_ip_address", i.PrivateIpAddress)
d.Set("public_ip_address", i.PublicIpAddress)
return nil
}
func resourceAwsLightsailInstanceDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
resp, err := conn.DeleteInstance(&lightsail.DeleteInstanceInput{
InstanceName: aws.String(d.Id()),
})
if err != nil {
return err
}
op := resp.Operations[0]
stateConf := &resource.StateChangeConf{
Pending: []string{"Started"},
Target: []string{"Completed", "Succeeded"},
Refresh: resourceAwsLightsailInstanceOperationRefreshFunc(op.Id, meta),
Timeout: 10 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf(
"Error waiting for instance (%s) to become destroyed: %s",
d.Id(), err)
}
d.SetId("")
return nil
}
// method to check the status of an Operation, which is returned from
// Create/Delete methods.
// Status's are an aws.OperationStatus enum:
// - NotStarted
// - Started
// - Failed
// - Completed
// - Succeeded (not documented?)
func resourceAwsLightsailInstanceOperationRefreshFunc(
oid *string, meta interface{}) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
conn := meta.(*AWSClient).lightsailconn
log.Printf("[DEBUG] Checking if Lightsail Instance Operation (%s) is Completed", *oid)
o, err := conn.GetOperation(&lightsail.GetOperationInput{
OperationId: oid,
})
if err != nil {
return o, "FAILED", err
}
if o.Operation == nil {
return nil, "Failed", fmt.Errorf("[ERR] Error retrieving Operation info for operation (%s)", *oid)
}
log.Printf("[DEBUG] Lightsail Instance Operation (%s) is currently %q", *oid, *o.Operation.Status)
return o, *o.Operation.Status, nil
}
}

View File

@ -0,0 +1,151 @@
package aws
import (
"errors"
"fmt"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSLightsailInstance_basic(t *testing.T) {
var conf lightsail.Instance
lightsailName := fmt.Sprintf("tf-test-lightsail-%d", acctest.RandInt())
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "aws_lightsail_instance.lightsail_instance_test",
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLightsailInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLightsailInstanceConfig_basic(lightsailName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLightsailInstanceExists("aws_lightsail_instance.lightsail_instance_test", &conf),
resource.TestCheckResourceAttrSet("aws_lightsail_instance.lightsail_instance_test", "availability_zone"),
resource.TestCheckResourceAttrSet("aws_lightsail_instance.lightsail_instance_test", "blueprint_id"),
resource.TestCheckResourceAttrSet("aws_lightsail_instance.lightsail_instance_test", "bundle_id"),
resource.TestCheckResourceAttrSet("aws_lightsail_instance.lightsail_instance_test", "key_pair_name"),
),
},
},
})
}
func TestAccAWSLightsailInstance_disapear(t *testing.T) {
var conf lightsail.Instance
lightsailName := fmt.Sprintf("tf-test-lightsail-%d", acctest.RandInt())
testDestroy := func(*terraform.State) error {
// reach out and DELETE the Instance
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
_, err := conn.DeleteInstance(&lightsail.DeleteInstanceInput{
InstanceName: aws.String(lightsailName),
})
if err != nil {
return fmt.Errorf("Error deleting Lightsail Instance in disapear test")
}
// sleep 7 seconds to give it time, so we don't have to poll
time.Sleep(7 * time.Second)
return nil
}
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLightsailInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLightsailInstanceConfig_basic(lightsailName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSLightsailInstanceExists("aws_lightsail_instance.lightsail_instance_test", &conf),
testDestroy,
),
ExpectNonEmptyPlan: true,
},
},
})
}
func testAccCheckAWSLightsailInstanceExists(n string, res *lightsail.Instance) 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 errors.New("No LightsailInstance ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
respInstance, err := conn.GetInstance(&lightsail.GetInstanceInput{
InstanceName: aws.String(rs.Primary.Attributes["name"]),
})
if err != nil {
return err
}
if respInstance == nil || respInstance.Instance == nil {
return fmt.Errorf("Instance (%s) not found", rs.Primary.Attributes["name"])
}
*res = *respInstance.Instance
return nil
}
}
func testAccCheckAWSLightsailInstanceDestroy(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_lightsail_instance" {
continue
}
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
respInstance, err := conn.GetInstance(&lightsail.GetInstanceInput{
InstanceName: aws.String(rs.Primary.Attributes["name"]),
})
if err == nil {
if respInstance.Instance != nil {
return fmt.Errorf("LightsailInstance %q still exists", rs.Primary.ID)
}
}
// Verify the error
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NotFoundException" {
return nil
}
}
return err
}
return nil
}
func testAccAWSLightsailInstanceConfig_basic(lightsailName string) string {
return fmt.Sprintf(`
provider "aws" {
region = "us-east-1"
}
resource "aws_lightsail_instance" "lightsail_instance_test" {
name = "%s"
availability_zone = "us-east-1b"
blueprint_id = "gitlab_8_12_6"
bundle_id = "nano_1_0"
}
`, lightsailName)
}

9204
vendor/github.com/aws/aws-sdk-go/service/lightsail/api.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package lightsail
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
)
// Amazon Lightsail is the easiest way to get started with AWS for developers
// who just need virtual private servers. Lightsail includes everything you
// need to launch your project quickly - a virtual machine, SSD-based storage,
// data transfer, DNS management, and a static IP - for a low, predictable price.
// You manage those Lightsail servers through the Lightsail console or by using
// the API or command-line interface (CLI).
//
// For more information about Lightsail concepts and tasks, see the Lightsail
// Dev Guide (http://lightsail.aws.amazon.com/ls/docs).
//
// To use the Lightsail API or the CLI, you will need to use AWS Identity and
// Access Management (IAM) to generate access keys. For details about how to
// set this up, see the Lightsail Dev Guide (http://lightsail.aws.amazon.com/ls/docs/how-to/articles/lightsail-how-to-set-up-access-keys-to-use-sdk-api-cli).
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type Lightsail struct {
*client.Client
}
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// A ServiceName is the name of the service the client will make API calls to.
const ServiceName = "lightsail"
// New creates a new instance of the Lightsail client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a Lightsail client from just a session.
// svc := lightsail.New(mySession)
//
// // Create a Lightsail client with additional configuration
// svc := lightsail.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *Lightsail {
c := p.ClientConfig(ServiceName, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *Lightsail {
svc := &Lightsail{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2016-11-28",
JSONVersion: "1.1",
TargetPrefix: "Lightsail_20161128",
},
handlers,
),
}
// Handlers
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler)
svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler)
svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler)
svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return svc
}
// newRequest creates a new request for a Lightsail operation and runs any
// custom request initialization.
func (c *Lightsail) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
// Run custom request initialization if present
if initRequest != nil {
initRequest(req)
}
return req
}

8
vendor/vendor.json vendored
View File

@ -856,6 +856,14 @@
"version": "=v1.5.12",
"versionExact": "v1.5.12"
},
{
"checksumSHA1": "Zc40RDOSS1SZjt/YJebeWamE7+k=",
"path": "github.com/aws/aws-sdk-go/service/lightsail",
"revision": "e7849863adae563900a3474ee6feed65471ab070",
"revisionTime": "2016-11-30T19:57:18Z",
"version": "=v1.5.12",
"versionExact": "v1.5.12"
},
{
"checksumSHA1": "BZY0NsRwwIbs52ZSjaEGqgWHXyo=",
"path": "github.com/aws/aws-sdk-go/service/opsworks",

View File

@ -0,0 +1,90 @@
---
layout: "aws"
page_title: "AWS: aws_lightsail_instance"
sidebar_current: "docs-aws-resource-lightsail-instance"
description: |-
Provides an Lightsail Instance
---
# aws\_lightsail\_instance
Provides a Lightsail Instance. Amazon Lightsail is a service to provide easy virtual private servers
with custom software already setup. See [What is Amazon Lightsail?](https://lightsail.aws.amazon.com/ls/docs/getting-started/article/what-is-amazon-lightsail)
for more information.
Note: Lightsail is currently only supported in `us-east-1` region.
## Example Usage
```
# Create a new GitLab Lightsail Instance
resource "aws_lightsail_instance" "gitlab_test" {
name = "custom gitlab"
availability_zone = "us-east-1b"
blueprint_id = "string"
bundle_id = "string"
key_pair_name = "some_key_name"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the Lightsail Instance
* `availability_zone` - (Required) The Availability Zone in which to create your
instance. At this time, must be in `us-east-1` region
* `blueprint_id` - (Required) The ID for a virtual private server image
(see list below)
* `bundle_id` - (Required) The bundle of specification information (see list below)
* `key_pair_name` - (Required) The name of your key pair. Created in the
Lightsail console (cannot use `aws_key_pair` at this time)
* `user_data` - (Optional) launch script to configure server with additional user data
## Blueprints
Lightsail currently supports the following Blueprint IDs:
- `amazon_linux_2016_09_0`
- `ubuntu_16_04`
- `wordpress_4_6_1`
- `lamp_5_6_27`
- `nodejs_6_9_1`
- `joomla_3_6_3`
- `magento_2_1_2`
- `mean_3_2_10`
- `drupal_8_2_1`
- `gitlab_8_12_6`
- `redmine_3_3_1`
- `nginx_1_10_2`
## Bundles
Lightsail currently supports the following Bundle IDs:
- `nano_1_0`
- `micro_1_0`
- `small_1_0`
- `medium_1_0`
- `large_1_0`
## Attributes Reference
The following attributes are exported in addition to the arguments listed above:
* `id` - The ARN of the Lightsail instance (matches `arn`).
* `arn` - The ARN of the Lightsail instance (matches `id`).
* `availability_zone`
* `blueprint_id`
* `bundle_id`
* `key_pair_name`
* `user_data`
## Import
Lightsail Instances can be imported using their ARN, e.g.
```
$ terraform import aws_lightsail_instance.bar <arn>
```

View File

@ -686,6 +686,17 @@
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-lightsail/) %>>
<a href="#">Lightsail Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-lightsail-instance") %>>
<a href="/docs/providers/aws/r/lightsail_instance.html">aws_lightsail_instance</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-opsworks/) %>>
<a href="#">OpsWorks Resources</a>
<ul class="nav nav-visible">