diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4304656 --- /dev/null +++ b/go.mod @@ -0,0 +1,16 @@ +module paas + +go 1.24.1 + +require github.com/spf13/cobra v1.9.1 + +require ( + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect +) + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 + github.com/spf13/pflag v1.0.6 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..27567f7 --- /dev/null +++ b/go.sum @@ -0,0 +1,22 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..2f9eed2 --- /dev/null +++ b/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + "os" + "paas/pkg/argocd" + "paas/pkg/datadog" + "time" + + "github.com/spf13/cobra" +) + +const ( + downtimeDuration = 5 * time.Minute +) + +var ( + appName string + + syncCommand = &cobra.Command{ + Use: "sync", + Short: "sync an application with argocd", + RunE: func(cmd *cobra.Command, args []string) error { + app := datadog.NewDatadogApp(datadog.WithName(appName), datadog.WithStartDate(time.Now()), datadog.WithEndDate(time.Now().Add(downtimeDuration))) + + if err := app.ActivateDowntime(); err != nil { + return err + } + + argoApp := argocd.NewArgocdAppHandler() + + if err := argoApp.Sync(&argocd.ArgocdAppReq{Name: appName}); err != nil { + return fmt.Errorf("error while syncing argocd %s app, %w", appName, err) + } + + if err := app.RemoveDownTime(); err != nil { + return err + } + + return nil + }, + } +) + +func initSyncCommand() *cobra.Command { + syncCommand.Flags().StringVarP(&appName, "app", "a", "", "name of the application to sync") + syncCommand.MarkFlagRequired("app") + + return syncCommand +} + +func main() { + cmd := initSyncCommand() + + if err := cmd.Execute(); err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } +} diff --git a/pkg/argocd/application.go b/pkg/argocd/application.go new file mode 100644 index 0000000..c3e2007 --- /dev/null +++ b/pkg/argocd/application.go @@ -0,0 +1,29 @@ +package argocd + +import "fmt" + +type ( + ArgocdApp interface { + Sync(req *ArgocdAppReq) error + } + + ArgocdAppReq struct { + Name string + } + + argocdAppHandler struct{} +) + +func NewArgocdAppHandler() ArgocdApp { + return &argocdAppHandler{} +} + +func (a *argocdAppHandler) Sync(req *ArgocdAppReq) error { + if req.Name == "" { + return fmt.Errorf("no application name specified") + } + + // Call argocd... + + return nil +} diff --git a/pkg/datadog/application.go b/pkg/datadog/application.go new file mode 100644 index 0000000..bc2ab48 --- /dev/null +++ b/pkg/datadog/application.go @@ -0,0 +1,103 @@ +package datadog + +import ( + "encoding/json" + "fmt" + "time" +) + +type ( + DatadogApp interface { + Get() *DatadogAppBody + ActivateDowntime() error + RemoveDownTime() error + } + + DatadogAppBody struct { + Name string + Downtime time.Time + } + + datadogAppHandler struct { + Name string + StartDate time.Time + EndDate time.Time + } + + Option func(*datadogAppHandler) +) + +func NewDatadogApp(opts ...Option) DatadogApp { + app := &datadogAppHandler{} + for _, opt := range opts { + opt(app) + } + + return app +} + +func WithName(name string) Option { + return func(d *datadogAppHandler) { + d.Name = name + } +} + +func WithStartDate(start time.Time) Option { + return func(d *datadogAppHandler) { + d.StartDate = start + } +} + +func WithEndDate(end time.Time) Option { + return func(d *datadogAppHandler) { + d.EndDate = end + } +} + +func (d *datadogAppHandler) Get() *DatadogAppBody { + return &DatadogAppBody{Name: d.Name, Downtime: d.StartDate} +} + +func (d *datadogAppHandler) ActivateDowntime() error { + remoteApp := d.Get() + if remoteApp.Downtime.Unix() != 0 { + fmt.Printf("%s app already got a downtime", d.Name) + return nil + } + + if d.Name == "" { + return fmt.Errorf("no app name specified") + } + + if d.EndDate.Before(d.StartDate) { + return fmt.Errorf("end date is before start date") + } + + _, err := d.Marshal() + if err != nil { + return err + } + + // Do datadog call + + return nil +} + +func (d *datadogAppHandler) RemoveDownTime() error { + remoteApp := d.Get() + if remoteApp.Downtime.Unix() == 0 { + fmt.Printf("no downtime specified for %s, skipping", d.Name) + return nil + } + + return nil +} + +func (d *datadogAppHandler) Marshal() ([]byte, error) { + content, err := json.Marshal(d) + if err != nil { + return nil, fmt.Errorf("unable to parse datadog downtime request, %w", err) + } + + return content, nil +}