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

Adds SFTP logging endpoint support #77

Merged
merged 1 commit into from
May 20, 2020
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
6 changes: 6 additions & 0 deletions pkg/api/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ type Interface interface {
UpdateHeroku(*fastly.UpdateHerokuInput) (*fastly.Heroku, error)
DeleteHeroku(*fastly.DeleteHerokuInput) error

CreateSFTP(*fastly.CreateSFTPInput) (*fastly.SFTP, error)
ListSFTPs(*fastly.ListSFTPsInput) ([]*fastly.SFTP, error)
GetSFTP(*fastly.GetSFTPInput) (*fastly.SFTP, error)
UpdateSFTP(*fastly.UpdateSFTPInput) (*fastly.SFTP, error)
DeleteSFTP(*fastly.DeleteSFTPInput) error

GetUser(*fastly.GetUserInput) (*fastly.User, error)

GetRegions() (*fastly.RegionsResponse, error)
Expand Down
15 changes: 15 additions & 0 deletions pkg/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/fastly/cli/pkg/logging/papertrail"
"github.com/fastly/cli/pkg/logging/s3"
"github.com/fastly/cli/pkg/logging/scalyr"
"github.com/fastly/cli/pkg/logging/sftp"
"github.com/fastly/cli/pkg/logging/splunk"
"github.com/fastly/cli/pkg/logging/sumologic"
"github.com/fastly/cli/pkg/logging/syslog"
Expand Down Expand Up @@ -223,6 +224,13 @@ func Run(args []string, env config.Environment, file config.File, configFilePath
herokuUpdate := heroku.NewUpdateCommand(herokuRoot.CmdClause, &globals)
herokuDelete := heroku.NewDeleteCommand(herokuRoot.CmdClause, &globals)

sftpRoot := sftp.NewRootCommand(loggingRoot.CmdClause, &globals)
sftpCreate := sftp.NewCreateCommand(sftpRoot.CmdClause, &globals)
sftpList := sftp.NewListCommand(sftpRoot.CmdClause, &globals)
sftpDescribe := sftp.NewDescribeCommand(sftpRoot.CmdClause, &globals)
sftpUpdate := sftp.NewUpdateCommand(sftpRoot.CmdClause, &globals)
sftpDelete := sftp.NewDeleteCommand(sftpRoot.CmdClause, &globals)

statsRoot := stats.NewRootCommand(app, &globals)
statsRegions := stats.NewRegionsCommand(statsRoot.CmdClause, &globals)
statsHistorical := stats.NewHistoricalCommand(statsRoot.CmdClause, &globals)
Expand Down Expand Up @@ -370,6 +378,13 @@ func Run(args []string, env config.Environment, file config.File, configFilePath
herokuUpdate,
herokuDelete,

sftpRoot,
sftpCreate,
sftpList,
sftpDescribe,
sftpUpdate,
sftpDelete,

statsRoot,
statsRegions,
statsHistorical,
Expand Down
113 changes: 113 additions & 0 deletions pkg/app/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1628,6 +1628,119 @@ COMMANDS
--version=VERSION Number of service version
-n, --name=NAME The name of the Heroku logging object

logging sftp create --name=NAME --version=VERSION --address=ADDRESS --user=USER --ssh-known-hosts=SSH-KNOWN-HOSTS [<flags>]
Create an SFTP logging endpoint on a Fastly service version

-n, --name=NAME The name of the SFTP logging object. Used as a
primary key for API access
-s, --service-id=SERVICE-ID Service ID
--version=VERSION Number of service version
--address=ADDRESS The hostname or IPv4 addres
--user=USER The username for the server
--ssh-known-hosts=SSH-KNOWN-HOSTS
A list of host keys for all hosts we can
connect to over SFTP
--port=PORT The port number
--password=PASSWORD The password for the server. If both password
and secret_key are passed, secret_key will be
used in preference
--public-key=PUBLIC-KEY A PGP public key that Fastly will use to
encrypt your log files before writing them to
disk
--secret-key=SECRET-KEY The SSH private key for the server. If both
password and secret_key are passed, secret_key
will be used in preference
--path=PATH The path to upload logs to. The directory must
exist on the SFTP server before logs can be
saved to it
--period=PERIOD How frequently log files are finalized so they
can be available for reading (in seconds,
default 3600)
--format=FORMAT Apache style log formatting
--format-version=FORMAT-VERSION
The version of the custom logging format used
for the configured endpoint. Can be either 2
(default) or 1
--gzip-level=GZIP-LEVEL What level of GZIP encoding to have when
dumping logs (default 0, no compression)
--response-condition=RESPONSE-CONDITION
The name of an existing condition in the
configured endpoint, or leave blank to always
execute
--timestamp-format=TIMESTAMP-FORMAT
strftime specified timestamp formatting
(default "%Y-%m-%dT%H:%M:%S.000")
--placement=PLACEMENT Where in the generated VCL the logging call
should be placed, overriding any format_version
default. Can be none or waf_debug

logging sftp list --version=VERSION [<flags>]
List SFTP endpoints on a Fastly service version

-s, --service-id=SERVICE-ID Service ID
--version=VERSION Number of service version

logging sftp describe --version=VERSION --name=NAME [<flags>]
Show detailed information about an SFTP logging endpoint on a Fastly service
version

-s, --service-id=SERVICE-ID Service ID
--version=VERSION Number of service version
-d, --name=NAME The name of the SFTP logging object
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like -n would be more appropriate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ezkl I agree with you. However, using -d was adopted from a standard set by s3 for the describe subcommand.

While it may not be a big deal, this change would be backward incompatible --- unless we added -n --- for anyone using the cli in scripts, etc.

I'm assuming there was a reason for explicitly using -d for describe cc @phamann.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha very good catch, I think that was a copy/pasta error in the S3 implementation and we should use -n. I'm also happy with the breaking change to S3, which I can follow up with in a separate PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@phamann I can make this change across all logging endpoints after we get them all merged in.


logging sftp update --version=VERSION --name=NAME [<flags>]
Update an SFTP logging endpoint on a Fastly service version

-s, --service-id=SERVICE-ID Service ID
--version=VERSION Number of service version
-n, --name=NAME The name of the SFTP logging object
--new-name=NEW-NAME New name of the SFTP logging object
--address=ADDRESS The hostname or IPv4 address
--port=PORT The port number
--public-key=PUBLIC-KEY A PGP public key that Fastly will use to
encrypt your log files before writing them to
disk
--secret-key=SECRET-KEY The SSH private key for the server. If both
password and secret_key are passed, secret_key
will be used in preference
--ssh-known-hosts=SSH-KNOWN-HOSTS
A list of host keys for all hosts we can
connect to over SFTP
--user=USER The username for the server
--password=PASSWORD The password for the server. If both password
and secret_key are passed, secret_key will be
used in preference
--path=PATH The path to upload logs to. The directory must
exist on the SFTP server before logs can be
saved to it
--period=PERIOD How frequently log files are finalized so they
can be available for reading (in seconds,
default 3600)
--format=FORMAT Apache style log formatting
--format-version=FORMAT-VERSION
The version of the custom logging format used
for the configured endpoint. Can be either 2
(default) or 1
--gzip-level=GZIP-LEVEL What level of GZIP encoding to have when
dumping logs (default 0, no compression)
--response-condition=RESPONSE-CONDITION
The name of an existing condition in the
configured endpoint, or leave blank to always
execute
--timestamp-format=TIMESTAMP-FORMAT
strftime specified timestamp formatting
(default "%Y-%m-%dT%H:%M:%S.000")
--placement=PLACEMENT Where in the generated VCL the logging call
should be placed, overriding any format_version
default. Can be none or waf_debug

logging sftp delete --version=VERSION --name=NAME [<flags>]
Delete an SFTP logging endpoint on a Fastly service version

-s, --service-id=SERVICE-ID Service ID
--version=VERSION Number of service version
-n, --name=NAME The name of the SFTP logging object

stats regions
List stats regions

Expand Down
154 changes: 154 additions & 0 deletions pkg/logging/sftp/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package sftp

import (
"io"

"github.com/fastly/cli/pkg/common"
"github.com/fastly/cli/pkg/compute/manifest"
"github.com/fastly/cli/pkg/config"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/fastly"
)

// CreateCommand calls the Fastly API to create SFTP logging endpoints.
type CreateCommand struct {
common.Base
manifest manifest.Data

// required
EndpointName string // Can't shaddow common.Base method Name().
Version int
Address string
User string
SSHKnownHosts string

// optional
Port common.OptionalUint
Password common.OptionalString
PublicKey common.OptionalString
SecretKey common.OptionalString
Path common.OptionalString
Period common.OptionalUint
Format common.OptionalString
FormatVersion common.OptionalUint
GzipLevel common.OptionalUint
ResponseCondition common.OptionalString
TimestampFormat common.OptionalString
Placement common.OptionalString
}

// NewCreateCommand returns a usable command registered under the parent.
func NewCreateCommand(parent common.Registerer, globals *config.Data) *CreateCommand {
var c CreateCommand

c.Globals = globals
c.manifest.File.Read(manifest.Filename)
c.CmdClause = parent.Command("create", "Create an SFTP logging endpoint on a Fastly service version").Alias("add")

c.CmdClause.Flag("name", "The name of the SFTP logging object. Used as a primary key for API access").Short('n').Required().StringVar(&c.EndpointName)
c.CmdClause.Flag("service-id", "Service ID").Short('s').StringVar(&c.manifest.Flag.ServiceID)
c.CmdClause.Flag("version", "Number of service version").Required().IntVar(&c.Version)

c.CmdClause.Flag("address", "The hostname or IPv4 addres").Required().StringVar(&c.Address)
c.CmdClause.Flag("user", "The username for the server").Required().StringVar(&c.User)
c.CmdClause.Flag("ssh-known-hosts", "A list of host keys for all hosts we can connect to over SFTP").Required().StringVar(&c.SSHKnownHosts)

c.CmdClause.Flag("port", "The port number").Action(c.Port.Set).UintVar(&c.Port.Value)
mccurdyc marked this conversation as resolved.
Show resolved Hide resolved
c.CmdClause.Flag("password", "The password for the server. If both password and secret_key are passed, secret_key will be used in preference").Action(c.Password.Set).StringVar(&c.Password.Value)
c.CmdClause.Flag("public-key", "A PGP public key that Fastly will use to encrypt your log files before writing them to disk").Action(c.PublicKey.Set).StringVar(&c.PublicKey.Value)
c.CmdClause.Flag("secret-key", "The SSH private key for the server. If both password and secret_key are passed, secret_key will be used in preference").Action(c.SecretKey.Set).StringVar(&c.SecretKey.Value)
c.CmdClause.Flag("path", "The path to upload logs to. The directory must exist on the SFTP server before logs can be saved to it").Action(c.Path.Set).StringVar(&c.Path.Value)
c.CmdClause.Flag("period", "How frequently log files are finalized so they can be available for reading (in seconds, default 3600)").Action(c.Period.Set).UintVar(&c.Period.Value)
c.CmdClause.Flag("format", "Apache style log formatting").Action(c.Format.Set).StringVar(&c.Format.Value)
c.CmdClause.Flag("format-version", "The version of the custom logging format used for the configured endpoint. Can be either 2 (default) or 1").Action(c.FormatVersion.Set).UintVar(&c.FormatVersion.Value)
c.CmdClause.Flag("gzip-level", "What level of GZIP encoding to have when dumping logs (default 0, no compression)").Action(c.GzipLevel.Set).UintVar(&c.GzipLevel.Value)
c.CmdClause.Flag("response-condition", "The name of an existing condition in the configured endpoint, or leave blank to always execute").Action(c.ResponseCondition.Set).StringVar(&c.ResponseCondition.Value)
c.CmdClause.Flag("timestamp-format", `strftime specified timestamp formatting (default "%Y-%m-%dT%H:%M:%S.000")`).Action(c.TimestampFormat.Set).StringVar(&c.TimestampFormat.Value)
c.CmdClause.Flag("placement", "Where in the generated VCL the logging call should be placed, overriding any format_version default. Can be none or waf_debug").Action(c.Placement.Set).StringVar(&c.Placement.Value)

return &c
}

// createInput transforms values parsed from CLI flags into an object to be used by the API client library.
func (c *CreateCommand) createInput() (*fastly.CreateSFTPInput, error) {
var input fastly.CreateSFTPInput

serviceID, source := c.manifest.ServiceID()
if source == manifest.SourceUndefined {
return nil, errors.ErrNoServiceID
}

input.Service = serviceID
input.Version = c.Version
input.Name = fastly.String(c.EndpointName)
input.Address = fastly.String(c.Address)
input.User = fastly.String(c.User)
input.SSHKnownHosts = fastly.String(c.SSHKnownHosts)

if c.Port.Valid {
input.Port = fastly.Uint(c.Port.Value)
}

if c.Password.Valid {
input.Password = fastly.String(c.Password.Value)
}

if c.PublicKey.Valid {
input.PublicKey = fastly.String(c.PublicKey.Value)
}

if c.SecretKey.Valid {
input.SecretKey = fastly.String(c.SecretKey.Value)
}
mccurdyc marked this conversation as resolved.
Show resolved Hide resolved

if c.Path.Valid {
input.Path = fastly.String(c.Path.Value)
}

if c.Period.Valid {
input.Period = fastly.Uint(c.Period.Value)
}

if c.Format.Valid {
input.Format = fastly.String(c.Format.Value)
}

if c.FormatVersion.Valid {
input.FormatVersion = fastly.Uint(c.FormatVersion.Value)
}

if c.GzipLevel.Valid {
input.GzipLevel = fastly.Uint(c.GzipLevel.Value)
}

if c.ResponseCondition.Valid {
input.ResponseCondition = fastly.String(c.ResponseCondition.Value)
}

if c.TimestampFormat.Valid {
input.TimestampFormat = fastly.String(c.TimestampFormat.Value)
}

if c.Placement.Valid {
input.Placement = fastly.String(c.Placement.Value)
}

return &input, nil
}

// Exec invokes the application logic for the command.
func (c *CreateCommand) Exec(in io.Reader, out io.Writer) error {
input, err := c.createInput()
if err != nil {
return err
}

d, err := c.Globals.Client.CreateSFTP(input)
if err != nil {
return err
}

text.Success(out, "Created SFTP logging endpoint %s (service %s version %d)", d.Name, d.ServiceID, d.Version)
return nil
}
47 changes: 47 additions & 0 deletions pkg/logging/sftp/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package sftp

import (
"io"

"github.com/fastly/cli/pkg/common"
"github.com/fastly/cli/pkg/compute/manifest"
"github.com/fastly/cli/pkg/config"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/fastly"
)

// DeleteCommand calls the Fastly API to delete SFTP logging endpoints.
type DeleteCommand struct {
common.Base
manifest manifest.Data
Input fastly.DeleteSFTPInput
}

// NewDeleteCommand returns a usable command registered under the parent.
func NewDeleteCommand(parent common.Registerer, globals *config.Data) *DeleteCommand {
var c DeleteCommand
c.Globals = globals
c.manifest.File.Read(manifest.Filename)
c.CmdClause = parent.Command("delete", "Delete an SFTP logging endpoint on a Fastly service version").Alias("remove")
c.CmdClause.Flag("service-id", "Service ID").Short('s').StringVar(&c.manifest.Flag.ServiceID)
c.CmdClause.Flag("version", "Number of service version").Required().IntVar(&c.Input.Version)
c.CmdClause.Flag("name", "The name of the SFTP logging object").Short('n').Required().StringVar(&c.Input.Name)
return &c
}

// Exec invokes the application logic for the command.
func (c *DeleteCommand) Exec(in io.Reader, out io.Writer) error {
serviceID, source := c.manifest.ServiceID()
if source == manifest.SourceUndefined {
return errors.ErrNoServiceID
}
c.Input.Service = serviceID

if err := c.Globals.Client.DeleteSFTP(&c.Input); err != nil {
return err
}

text.Success(out, "Deleted SFTP logging endpoint %s (service %s version %d)", c.Input.Name, c.Input.Service, c.Input.Version)
return nil
}
Loading