Skip to content
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/fastly: GCS #13553

Merged
merged 8 commits into from
Apr 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions builtin/providers/fastly/resource_fastly_service_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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 {
Expand Down
131 changes: 131 additions & 0 deletions builtin/providers/fastly/resource_fastly_service_v1_gcslogging_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
19 changes: 19 additions & 0 deletions website/source/docs/providers/fastly/r/service_v1.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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