provider/fastly: GCS (#13553)

*  PAAS-5611 Adding docs

* PAAS-5611 adding gcs logging resource

* PAAS-5611 adding gcs testing

* Adding testing file

* Cleaning up type and making flatten array clearer

* Fixing issue with tests
This commit is contained in:
Michael Dunton 2017-04-21 12:44:25 -04:00 committed by Paul Stack
parent 5ce5348070
commit 8995f1afdb
3 changed files with 314 additions and 0 deletions

View File

@ -647,6 +647,72 @@ func resourceServiceV1() *schema.Resource {
},
},
"gcslogging": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
// Required fields
"name": {
Type: schema.TypeString,
Required: true,
Description: "Unique name to refer to this logging setup",
},
"email": {
Type: schema.TypeString,
Required: true,
Description: "The email address associated with the target GCS bucket on your account.",
},
"bucket_name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the bucket in which to store the logs.",
},
"secret_key": {
Type: schema.TypeString,
Required: true,
Description: "The secret key associated with the target gcs bucket on your account.",
},
// Optional fields
"path": {
Type: schema.TypeString,
Optional: true,
Description: "Path to store the files. Must end with a trailing slash",
},
"gzip_level": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
Description: "Gzip Compression level",
},
"period": {
Type: schema.TypeInt,
Optional: true,
Default: 3600,
Description: "How frequently the logs should be transferred, in seconds (Default 3600)",
},
"format": {
Type: schema.TypeString,
Optional: true,
Default: "%h %l %u %t %r %>s",
Description: "Apache-style string or VCL variables to use for log formatting",
},
"timestamp_format": {
Type: schema.TypeString,
Optional: true,
Default: "%Y-%m-%dT%H:%M:%S.000",
Description: "specified timestamp formatting (default `%Y-%m-%dT%H:%M:%S.000`)",
},
"response_condition": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "Name of a condition to apply this logging.",
},
},
},
},
"response_object": {
Type: schema.TypeSet,
Optional: true,
@ -1450,6 +1516,59 @@ func resourceServiceV1Update(d *schema.ResourceData, meta interface{}) error {
}
}
// find difference in gcslogging
if d.HasChange("gcslogging") {
os, ns := d.GetChange("gcslogging")
if os == nil {
os = new(schema.Set)
}
if ns == nil {
ns = new(schema.Set)
}
oss := os.(*schema.Set)
nss := ns.(*schema.Set)
removeGcslogging := oss.Difference(nss).List()
addGcslogging := nss.Difference(oss).List()
// DELETE old gcslogging configurations
for _, pRaw := range removeGcslogging {
sf := pRaw.(map[string]interface{})
opts := gofastly.DeleteGCSInput{
Service: d.Id(),
Version: latestVersion,
Name: sf["name"].(string),
}
log.Printf("[DEBUG] Fastly gcslogging removal opts: %#v", opts)
err := conn.DeleteGCS(&opts)
if err != nil {
return err
}
}
// POST new/updated gcslogging
for _, pRaw := range addGcslogging {
sf := pRaw.(map[string]interface{})
opts := gofastly.CreateGCSInput{
Service: d.Id(),
Version: latestVersion,
Name: sf["name"].(string),
User: sf["email"].(string),
Bucket: sf["bucket_name"].(string),
SecretKey: sf["secret_key"].(string),
Format: sf["format"].(string),
ResponseCondition: sf["response_condition"].(string),
}
log.Printf("[DEBUG] Create GCS Opts: %#v", opts)
_, err := conn.CreateGCS(&opts)
if err != nil {
return err
}
}
}
// find difference in Response Object
if d.HasChange("response_object") {
or, nr := d.GetChange("response_object")
@ -1883,6 +2002,22 @@ func resourceServiceV1Read(d *schema.ResourceData, meta interface{}) error {
log.Printf("[WARN] Error setting Sumologic for (%s): %s", d.Id(), err)
}
// refresh GCS Logging
log.Printf("[DEBUG] Refreshing GCS for (%s)", d.Id())
GCSList, err := conn.ListGCSs(&gofastly.ListGCSsInput{
Service: d.Id(),
Version: s.ActiveVersion.Number,
})
if err != nil {
return fmt.Errorf("[ERR] Error looking up GCS for (%s), version (%s): %s", d.Id(), s.ActiveVersion.Number, err)
}
gcsl := flattenGCS(GCSList)
if err := d.Set("gcs", gcsl); err != nil {
log.Printf("[WARN] Error setting gcs for (%s): %s", d.Id(), err)
}
// refresh Response Objects
log.Printf("[DEBUG] Refreshing Response Object for (%s)", d.Id())
responseObjectList, err := conn.ListResponseObjects(&gofastly.ListResponseObjectsInput{
@ -2350,6 +2485,35 @@ func flattenSumologics(sumologicList []*gofastly.Sumologic) []map[string]interfa
return l
}
func flattenGCS(gcsList []*gofastly.GCS) []map[string]interface{} {
var GCSList []map[string]interface{}
for _, currentGCS := range gcsList {
// Convert gcs to a map for saving to state.
GCSMapString := map[string]interface{}{
"name": currentGCS.Name,
"email": currentGCS.User,
"bucket_name": currentGCS.Bucket,
"secret_key": currentGCS.SecretKey,
"path": currentGCS.Path,
"period": int(currentGCS.Period),
"gzip_level": int(currentGCS.GzipLevel),
"response_condition": currentGCS.ResponseCondition,
"format": currentGCS.Format,
}
// prune any empty values that come from the default string value in structs
for k, v := range GCSMapString {
if v == "" {
delete(GCSMapString, k)
}
}
GCSList = append(GCSList, GCSMapString)
}
return GCSList
}
func flattenResponseObjects(responseObjectList []*gofastly.ResponseObject) []map[string]interface{} {
var rol []map[string]interface{}
for _, ro := range responseObjectList {

View File

@ -0,0 +1,131 @@
package fastly
import (
"fmt"
"reflect"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
gofastly "github.com/sethvargo/go-fastly"
)
func TestResourceFastlyFlattenGCS(t *testing.T) {
cases := []struct {
remote []*gofastly.GCS
local []map[string]interface{}
}{
{
remote: []*gofastly.GCS{
&gofastly.GCS{
Name: "GCS collector",
User: "email@example.com",
Bucket: "bucketName",
SecretKey: "secretKey",
Format: "log format",
Period: 3600,
GzipLevel: 0,
},
},
local: []map[string]interface{}{
map[string]interface{}{
"name": "GCS collector",
"email": "email@example.com",
"bucket_name": "bucketName",
"secret_key": "secretKey",
"format": "log format",
"period": 3600,
"gzip_level": 0,
},
},
},
}
for _, c := range cases {
out := flattenGCS(c.remote)
if !reflect.DeepEqual(out, c.local) {
t.Fatalf("Error matching:\nexpected: %#v\ngot: %#v", c.local, out)
}
}
}
func TestAccFastlyServiceV1_gcslogging(t *testing.T) {
var service gofastly.ServiceDetail
name := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
gcsName := fmt.Sprintf("gcs %s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckServiceV1Destroy,
Steps: []resource.TestStep{
{
Config: testAccServiceV1Config_gcs(name, gcsName),
Check: resource.ComposeTestCheckFunc(
testAccCheckServiceV1Exists("fastly_service_v1.foo", &service),
testAccCheckFastlyServiceV1Attributes_gcs(&service, name, gcsName),
),
},
},
})
}
func testAccCheckFastlyServiceV1Attributes_gcs(service *gofastly.ServiceDetail, name, gcsName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if service.Name != name {
return fmt.Errorf("Bad name, expected (%s), got (%s)", name, service.Name)
}
conn := testAccProvider.Meta().(*FastlyClient).conn
gcsList, err := conn.ListGCSs(&gofastly.ListGCSsInput{
Service: service.ID,
Version: service.ActiveVersion.Number,
})
if err != nil {
return fmt.Errorf("[ERR] Error looking up GCSs for (%s), version (%s): %s", service.Name, service.ActiveVersion.Number, err)
}
if len(gcsList) != 1 {
return fmt.Errorf("GCS missing, expected: 1, got: %d", len(gcsList))
}
if gcsList[0].Name != gcsName {
return fmt.Errorf("GCS name mismatch, expected: %s, got: %#v", gcsName, gcsList[0].Name)
}
return nil
}
}
func testAccServiceV1Config_gcs(name, gcsName string) string {
backendName := fmt.Sprintf("%s.aws.amazon.com", acctest.RandString(3))
return fmt.Sprintf(`
resource "fastly_service_v1" "foo" {
name = "%s"
domain {
name = "test.notadomain.com"
comment = "tf-testing-domain"
}
backend {
address = "%s"
name = "tf -test backend"
}
gcslogging {
name = "%s"
email = "email@example.com",
bucket_name = "bucketName",
secret_key = "secretKey",
format = "log format",
response_condition = "",
}
force_destroy = true
}`, name, backendName, gcsName)
}

View File

@ -155,6 +155,8 @@ Defined below.
Defined below.
* `sumologic` - (Optional) A Sumologic endpoint to send streaming logs too.
Defined below.
* `gcslogging` - (Optional) A gcs endpoint to send streaming logs too.
Defined below.
* `response_object` - (Optional) Allows you to create synthetic responses that exist entirely on the varnish machine. Useful for creating error or maintenance pages that exists outside the scope of your datacenter. Best when used with Condition objects.
* `vcl` - (Optional) A set of custom VCL configuration blocks. The
ability to upload custom VCL code is not enabled by default for new Fastly
@ -326,6 +328,22 @@ The `sumologic` block supports:
* `response_condition` - (Optional) Name of already defined `condition` to apply. This `condition` must be of type `RESPONSE`. For detailed information about Conditionals, see [Fastly's Documentation on Conditionals][fastly-conditionals].
* `message_type` - (Optional) How the message should be formatted. One of: classic, loggly, logplex, blank. See [Fastly's Documentation on Sumologic][fastly-sumologic]
The `gcslogging` block supports:
* `name` - (Required) A unique name to identify this GCS endpoint.
* `email` - (Required) The email address associated with the target GCS bucket on your account.
* `bucket_name` - (Required) The name of the bucket in which to store the logs.
* `secret_key` - (Required) The secret key associated with the target gcs bucket on your account.
* `path` - (Optional) Path to store the files. Must end with a trailing slash.
If this field is left empty, the files will be saved in the bucket's root path.
* `period` - (Optional) How frequently the logs should be transferred, in
seconds. Default `3600`.
* `gzip_level` - (Optional) Level of GZIP compression, from `0-9`. `0` is no
compression. `1` is fastest and least compressed, `9` is slowest and most
compressed. Default `0`.
* `format` - (Optional) Apache-style string or VCL variables to use for log formatting. Defaults to Apache Common Log format (`%h %l %u %t %r %>s`)
* `response_condition` - (Optional) Name of already defined `condition` to apply. This `condition` must be of type `RESPONSE`. For detailed information about Conditionals, see [Fastly's Documentation on Conditionals][fastly-conditionals].
The `response_object` block supports:
* `name` - (Required) A unique name to identify this Response Object.
@ -369,3 +387,4 @@ Service.
[fastly-cname]: https://docs.fastly.com/guides/basic-setup/adding-cname-records
[fastly-conditionals]: https://docs.fastly.com/guides/conditions/using-conditions
[fastly-sumologic]: https://docs.fastly.com/api/logging#logging_sumologic
[fastly-gcs]: https://docs.fastly.com/api/logging#logging_gcs