Skip to content

Commit

Permalink
Add support for FTP logging
Browse files Browse the repository at this point in the history
  • Loading branch information
ezkl authored and mccurdyc committed Jun 10, 2020
1 parent 154a2ae commit c1f0412
Show file tree
Hide file tree
Showing 4 changed files with 548 additions and 1 deletion.
268 changes: 268 additions & 0 deletions fastly/block_fastly_service_v1_logging_ftp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
package fastly

import (
"fmt"
"log"

fst "github.com/fastly/go-fastly/fastly"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

var ftpSchema = &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
// Required fields
"name": {
Type: schema.TypeString,
Required: true,
Description: "The unique name of the FTP logging endpoint",
},

"address": {
Type: schema.TypeString,
Required: true,
Description: "The FTP URL to stream logs to.",
},

"user": {
Type: schema.TypeString,
Required: true,
Description: "The username for the server (can be anonymous).",
},

"password": {
Type: schema.TypeString,
Required: true,
Description: "The password for the server (for anonymous use an email address).",
Sensitive: true,
},

"path": {
Type: schema.TypeString,
Required: true,
Description: "The path to upload log files to. If the path ends in / then it is treated as a directory.",
},

// Optional fields
"port": {
Type: schema.TypeInt,
Optional: true,
Default: 21,
Description: "The port number.",
},

"period": {
Type: schema.TypeInt,
Optional: true,
Default: 3600,
Description: "How frequently the logs should be transferred, in seconds (Default 3600)",
},

"public_key": {
Type: schema.TypeString,
Optional: true,
Description: "The PGP public key that Fastly will use to encrypt your log files before writing them to disk",
},

"gzip_level": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
Description: "Gzip Compression level",
},

"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`)",
},

"format": {
Type: schema.TypeString,
Optional: true,
Description: "Apache-style string or VCL variables to use for log formatting.",
},

"format_version": {
Type: schema.TypeInt,
Optional: true,
Default: 2,
Description: "The version of the custom logging format used for the configured endpoint. Can be either 1 or 2. (default: 2)",
ValidateFunc: validateLoggingFormatVersion(),
},

"placement": {
Type: schema.TypeString,
Optional: true,
Description: "Where in the generated VCL the logging call should be placed",
ValidateFunc: validateLoggingPlacement(),
},

"response_condition": {
Type: schema.TypeString,
Optional: true,
Description: "The name of the condition to apply",
},
},
},
}

func processFTP(d *schema.ResourceData, conn *fst.Client, latestVersion int) error {
serviceID := d.Id()
of, nf := d.GetChange("logging_ftp")

if of == nil {
of = new(schema.Set)
}
if nf == nil {
nf = new(schema.Set)
}

ofs := of.(*schema.Set)
nfs := nf.(*schema.Set)

removeFTPLogging := ofs.Difference(nfs).List()
addFTPLogging := nfs.Difference(ofs).List()

// DELETE old FTP logging endpoints
for _, oRaw := range removeFTPLogging {
of := oRaw.(map[string]interface{})
opts := buildDeleteFTP(of, serviceID, latestVersion)

log.Printf("[DEBUG] Fastly FTP logging endpoint removal opts: %#v", opts)

if err := deleteFTP(conn, opts); err != nil {
return err
}
}

// POST new/updated FTP logging endpoints
for _, nRaw := range addFTPLogging {
ef := nRaw.(map[string]interface{})
opts := buildCreateFTP(ef, serviceID, latestVersion)

log.Printf("[DEBUG] Fastly FTP logging addition opts: %#v", opts)

if err := createFTP(conn, opts); err != nil {
return err
}
}

return nil
}

func readFTP(conn *fst.Client, d *schema.ResourceData, s *fst.ServiceDetail) error {
// refresh FTP
log.Printf("[DEBUG] Refreshing FTP logging endpoints for (%s)", d.Id())
ftpList, err := conn.ListFTPs(&fst.ListFTPsInput{
Service: d.Id(),
Version: s.ActiveVersion.Number,
})

if err != nil {
return fmt.Errorf("[ERR] Error looking up FTP logging endpoints for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
}

ell := flattenFTP(ftpList)

if err := d.Set("logging_ftp", ell); err != nil {
log.Printf("[WARN] Error setting FTP logging endpoints for (%s): %s", d.Id(), err)
}

return nil
}

func createFTP(conn *fst.Client, i *fst.CreateFTPInput) error {
_, err := conn.CreateFTP(i)
if err != nil {
return err
}
return nil
}

func deleteFTP(conn *fst.Client, i *fst.DeleteFTPInput) error {
err := conn.DeleteFTP(i)

if errRes, ok := err.(*fst.HTTPError); ok {
if errRes.StatusCode != 404 {
return err
}
} else if err != nil {
return err
}

return nil
}

func flattenFTP(ftpList []*fst.FTP) []map[string]interface{} {
var fsl []map[string]interface{}
for _, fl := range ftpList {
// Convert FTP logging to a map for saving to state.
nfl := map[string]interface{}{
"name": fl.Name,
"address": fl.Address,
"user": fl.Username,
"password": fl.Password,
"path": fl.Path,
"port": fl.Port,
"period": fl.Period,
"public_key": fl.PublicKey,
"gzip_level": fl.GzipLevel,
"timestamp_format": fl.TimestampFormat,
"format": fl.Format,
"format_version": fl.FormatVersion,
"placement": fl.Placement,
"response_condition": fl.ResponseCondition,
}

// prune any empty values that come from the default string value in structs
for k, v := range nfl {
if v == "" {
delete(nfl, k)
}
}

fsl = append(fsl, nfl)
}

return fsl
}

func buildCreateFTP(ftpMap interface{}, serviceID string, serviceVersion int) *fst.CreateFTPInput {
df := ftpMap.(map[string]interface{})
opts := fst.CreateFTPInput{
Service: serviceID,
Version: serviceVersion,
Name: df["name"].(string),
Address: df["address"].(string),
Username: df["user"].(string),
Password: df["password"].(string),
Path: df["path"].(string),
Port: uint(df["port"].(int)),
Period: uint(df["period"].(int)),
PublicKey: df["public_key"].(string),
GzipLevel: uint8(df["gzip_level"].(int)),
TimestampFormat: df["timestamp_format"].(string),
Format: df["format"].(string),
FormatVersion: uint(df["format_version"].(int)),
Placement: df["placement"].(string),
ResponseCondition: df["response_condition"].(string),
}

return &opts
}

func buildDeleteFTP(ftpMap interface{}, serviceID string, serviceVersion int) *fst.DeleteFTPInput {
df := ftpMap.(map[string]interface{})

opts := fst.DeleteFTPInput{
Service: serviceID,
Version: serviceVersion,
Name: df["name"].(string),
}

return &opts
}
Loading

0 comments on commit c1f0412

Please sign in to comment.