diff --git a/README.md b/README.md index ec25473..482b795 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,8 @@ cloud-floating-ip -i 10.200.0.50 status When `cloud-floating-ip` runs on the target instance, most settings (region, instance id, cloud provider, ...) can be guessed from the instance metadata. - To act on a remote instance, we must be more explicit : + ```bash cloud-floating-ip -o aws -i 10.200.0.50 -t i-0e3f4ac17545ce580 -r eu-west-1 status cloud-floating-ip -o aws -i 10.200.0.50 -t i-0e3f4ac17545ce580 -r eu-west-1 preempt diff --git a/pkg/hoster/aws/aws.go b/pkg/hoster/aws/aws.go index 165b2a8..a611daa 100644 --- a/pkg/hoster/aws/aws.go +++ b/pkg/hoster/aws/aws.go @@ -2,9 +2,9 @@ package aws import ( "fmt" - "log" "github.com/bpineau/cloud-floating-ip/config" + "github.com/bpineau/cloud-floating-ip/pkg/log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/ec2metadata" @@ -18,6 +18,7 @@ type Hoster struct { sess *session.Session ec2s *ec2.EC2 routes []*ec2.RouteTable + log log.Logger enid *string cidr *string vpc string @@ -33,16 +34,17 @@ const ( ) // Init prepare an aws hoster for usage -func (h *Hoster) Init(conf *config.CfiConfig) { +func (h *Hoster) Init(conf *config.CfiConfig, logger log.Logger) { h.conf = conf + h.log = logger err := h.checkMissingParam() if err != nil { - log.Fatalf("Missing param: %v", err) + h.log.Fatalf("Missing param: %v", err) } h.sess, err = session.NewSession(aws.NewConfig().WithMaxRetries(3)) if err != nil { - log.Fatalf("Failed to initialize an AWS session: %v", err) + h.log.Fatalf("Failed to initialize an AWS session: %v", err) } metadata := ec2metadata.New(h.sess) @@ -50,7 +52,7 @@ func (h *Hoster) Init(conf *config.CfiConfig) { if h.conf.Region == "" { h.conf.Region, err = metadata.Region() if err != nil { - log.Fatalf("Failed to collect region from instance metadata: %v", err) + h.log.Fatalf("Failed to collect region from instance metadata: %v", err) } } @@ -59,7 +61,7 @@ func (h *Hoster) Init(conf *config.CfiConfig) { if h.conf.Instance == "" { h.conf.Instance, err = metadata.GetMetadata("instance-id") if err != nil { - log.Fatalf("Failed to collect instanceid from instance metadata: %v", err) + h.log.Fatalf("Failed to collect instanceid from instance metadata: %v", err) } } @@ -67,7 +69,7 @@ func (h *Hoster) Init(conf *config.CfiConfig) { err = h.getNetworkInfo() if err != nil { - log.Fatalf("Failed to collect network infos: %v", err) + h.log.Fatalf("Failed to collect network infos: %v", err) } } @@ -127,15 +129,11 @@ func (h *Hoster) OnThisHoster() bool { // Preempt takes over the floating IP address func (h *Hoster) Preempt() error { if h.Status() { - if !h.conf.Quiet { - fmt.Printf("Already primary, nothing to do\n") - } + h.log.Infof("Already primary, nothing to do\n") return nil } - if !h.conf.Quiet { - fmt.Printf("Preempting %s route(s)\n", h.conf.IP) - } + h.log.Infof("Preempting %s route(s)\n", h.conf.IP) // contrary to GCE, an EC2 VPC can have several routes tables for _, table := range h.routes { @@ -157,7 +155,7 @@ func (h *Hoster) Preempt() error { } if err != nil { - log.Fatalf("Failed to create a route: %v", err) + h.log.Fatalf("Failed to create a route: %v", err) } } @@ -196,10 +194,8 @@ func (h *Hoster) Destroy() error { DestinationCidrBlock: h.cidr, } - if !h.conf.Quiet { - fmt.Printf("Deleting route to %s from %s table\n", - *h.cidr, *table.RouteTableId) - } + h.log.Infof("Deleting route to %s from %s table\n", + *h.cidr, *table.RouteTableId) if h.conf.DryRun { continue @@ -267,10 +263,8 @@ func (h *Hoster) addRouteInTable(table *ec2.RouteTable, cidr *string, eni *strin NetworkInterfaceId: eni, } - if !h.conf.Quiet { - fmt.Printf("Creating route to %s via ENI %s in table %s\n", - *cidr, *eni, *table.RouteTableId) - } + h.log.Infof("Creating route to %s via ENI %s in table %s\n", + *cidr, *eni, *table.RouteTableId) if h.conf.DryRun { return nil @@ -287,10 +281,8 @@ func (h *Hoster) replaceRouteInTable(table *ec2.RouteTable, cidr *string, eni *s NetworkInterfaceId: eni, } - if !h.conf.Quiet { - fmt.Printf("Replacing route to %s via ENI %s in table %s\n", - *cidr, *eni, *table.RouteTableId) - } + h.log.Infof("Replacing route to %s via ENI %s in table %s\n", + *cidr, *eni, *table.RouteTableId) if h.conf.DryRun { return nil diff --git a/pkg/hoster/gce/gce.go b/pkg/hoster/gce/gce.go index d0c7ac6..5cb1e08 100644 --- a/pkg/hoster/gce/gce.go +++ b/pkg/hoster/gce/gce.go @@ -9,12 +9,12 @@ package gce import ( "context" "fmt" - "log" "net/http" "strings" "time" "github.com/bpineau/cloud-floating-ip/config" + "github.com/bpineau/cloud-floating-ip/pkg/log" "cloud.google.com/go/compute/metadata" "golang.org/x/oauth2/google" @@ -33,40 +33,42 @@ type Hoster struct { client *http.Client svc *compute.Service ctx *context.Context + log log.Logger network string rname string selflink string } // Init prepare a gce hoster for usage -func (h *Hoster) Init(conf *config.CfiConfig) { +func (h *Hoster) Init(conf *config.CfiConfig, logger log.Logger) { var err error ctx := context.Background() h.conf = conf + h.log = logger err = h.checkMissingParam() if err != nil { - log.Fatalf("Missing param: %v", err) + h.log.Fatalf("Missing param: %v\n", err) } if h.conf.Project == "" { h.conf.Project, err = metadata.ProjectID() if err != nil { - log.Fatalf("Failed to guess project id: %v", err) + h.log.Fatalf("Failed to guess project id: %v\n", err) } } if h.conf.Instance == "" { h.conf.Instance, err = metadata.InstanceName() if err != nil { - log.Fatalf("Failed to guess instance id: %v", err) + h.log.Fatalf("Failed to guess instance id: %v", err) } } if h.conf.Zone == "" { h.conf.Zone, err = metadata.Zone() if err != nil { - log.Fatalf("Failed to guess instance zone: %v", err) + h.log.Fatalf("Failed to guess instance zone: %v", err) } } @@ -76,12 +78,12 @@ func (h *Hoster) Init(conf *config.CfiConfig) { h.client, err = google.DefaultClient(*h.ctx, compute.CloudPlatformScope) if err != nil { - log.Fatal(err) + h.log.Fatalf("Failed to get default client %s", err) } h.svc, err = compute.New(h.client) if err != nil { - log.Fatal(err) + h.log.Fatalf("Failed to instantiate a compute client: %s", err) } h.network = h.getNetworkInterface() @@ -90,12 +92,12 @@ func (h *Hoster) Init(conf *config.CfiConfig) { func (h *Hoster) getNetworkInterface() string { inst, err := h.svc.Instances.Get(h.conf.Project, h.conf.Zone, h.conf.Instance).Context(*h.ctx).Do() if err != nil { - log.Fatalf("Failed to guess network link: %v", err) + h.log.Fatalf("Failed to guess network link: %v", err) } // TODO: support instances with several interfaces if len(inst.NetworkInterfaces) != 1 { - log.Fatal("For now, we don't support more than one interface") + h.log.Fatalf("For now, we don't support more than one interface") } return inst.NetworkInterfaces[0].Network @@ -109,15 +111,11 @@ func (h *Hoster) OnThisHoster() bool { // Preempt takes over the floating IP address func (h *Hoster) Preempt() error { if h.Status() { - if !h.conf.Quiet { - fmt.Printf("Already primary, nothing to do\n") - } + h.log.Infof("Already primary, nothing to do\n") return nil } - if !h.conf.Quiet { - fmt.Printf("Preempting %s route(s)\n", h.conf.IP) - } + h.log.Infof("Preempting %s route(s)\n", h.conf.IP) rb := &compute.Route{ Name: h.rname, @@ -129,13 +127,11 @@ func (h *Hoster) Preempt() error { // There's no "update" or "replace" in GCP routes API. err := h.Destroy() if err != nil { - log.Fatalf("Failed to delete the route: %v", err) + h.log.Fatalf("Failed to delete the route: %v", err) } - if !h.conf.Quiet { - fmt.Printf("Creating a route %s to %s via %s on %s network\n", - h.rname, h.conf.IP, h.selflink, h.network) - } + h.log.Infof("Creating a route %s to %s via %s on %s network\n", + h.rname, h.conf.IP, h.selflink, h.network) if h.conf.DryRun { return nil @@ -143,7 +139,7 @@ func (h *Hoster) Preempt() error { err = h.blockingWait(h.svc.Routes.Insert(h.conf.Project, rb).Do()) if err != nil { - log.Fatalf("Failed to create the route: %v", err) + h.log.Fatalf("Failed to create the route: %v", err) } return nil @@ -164,17 +160,14 @@ func (h *Hoster) Status() bool { } } - log.Fatalf("Failed to get route status: %v", err) + h.log.Fatalf("Failed to get route status: %v", err) return false } // Destroy remove route to the IP from our VPC func (h *Hoster) Destroy() error { - if !h.conf.Quiet { - fmt.Printf("Deleting route to %s from %s network\n", - h.conf.IP, h.network) - } + h.log.Infof("Deleting route to %s from %s network\n", h.conf.IP, h.network) if h.conf.DryRun { return nil diff --git a/pkg/hoster/hoster.go b/pkg/hoster/hoster.go index 1c34edc..76b0616 100644 --- a/pkg/hoster/hoster.go +++ b/pkg/hoster/hoster.go @@ -6,11 +6,12 @@ import ( "github.com/bpineau/cloud-floating-ip/config" "github.com/bpineau/cloud-floating-ip/pkg/hoster/aws" "github.com/bpineau/cloud-floating-ip/pkg/hoster/gce" + "github.com/bpineau/cloud-floating-ip/pkg/log" ) // Hoster represents an hosting provider (aws or gce) type Hoster interface { - Init(conf *config.CfiConfig) + Init(conf *config.CfiConfig, logger log.Logger) OnThisHoster() bool Preempt() error Status() bool diff --git a/pkg/log/console/console.go b/pkg/log/console/console.go new file mode 100644 index 0000000..51ec484 --- /dev/null +++ b/pkg/log/console/console.go @@ -0,0 +1,31 @@ +package console + +import ( + "fmt" + "os" +) + +// Logger implements Logger interface, display logs on stdout +type Logger struct { + Quiet bool +} + +// Infof displays a formated string, honoring the Quiet config setting +func (l *Logger) Infof(format string, v ...interface{}) { + if l.Quiet { + return + } + fmt.Printf(format, v...) +} + +// Fatal displays a message then exit the program +func (l *Logger) Fatal(v ...interface{}) { + fmt.Print(v...) + os.Exit(1) +} + +// Fatalf displays a formated string then exit the program +func (l *Logger) Fatalf(format string, v ...interface{}) { + fmt.Printf(format, v...) + os.Exit(1) +} diff --git a/pkg/log/log.go b/pkg/log/log.go new file mode 100644 index 0000000..aebbf44 --- /dev/null +++ b/pkg/log/log.go @@ -0,0 +1,8 @@ +package log + +// Logger handle logs, ideally honoring the Quiet config parameter +type Logger interface { + Infof(format string, v ...interface{}) + Fatalf(format string, v ...interface{}) + Fatal(v ...interface{}) +} diff --git a/pkg/run/run.go b/pkg/run/run.go index bdc0dcd..83bf625 100644 --- a/pkg/run/run.go +++ b/pkg/run/run.go @@ -2,10 +2,10 @@ package run import ( "fmt" - "log" "github.com/bpineau/cloud-floating-ip/config" "github.com/bpineau/cloud-floating-ip/pkg/hoster" + "github.com/bpineau/cloud-floating-ip/pkg/log/console" "github.com/bpineau/cloud-floating-ip/pkg/operation" ) @@ -13,12 +13,14 @@ import ( func Run(conf *config.CfiConfig, op operation.CfiOperation) { var err error + log := &console.Logger{Quiet: conf.Quiet} + h, err := hoster.GuessHoster(conf.Hoster) if err != nil { log.Fatalf("Can't guess hoster, please specify '-o' option: %v", err) } - h.Init(conf) + h.Init(conf, log) switch op { case operation.CfiPreempt: