Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
keroxp committed May 27, 2024
1 parent 530797f commit bddefa6
Show file tree
Hide file tree
Showing 33 changed files with 1,225 additions and 843 deletions.
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[*.go]
indent_size = 4
indent_size = 2
indent_style = tab
[Shakefile]
indent_size = 2
indent_style = tab
indent_style = tab
37 changes: 25 additions & 12 deletions cage.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cage

import (
"context"
"time"

"github.com/loilo-inc/canarycage/awsiface"
)
Expand All @@ -10,27 +11,39 @@ type Cage interface {
Up(ctx context.Context) (*UpResult, error)
Run(ctx context.Context, input *RunInput) (*RunResult, error)
RollOut(ctx context.Context) (*RollOutResult, error)
Recreate(ctx context.Context) (*RecreateResult, error)
}

type Time interface {
Now() time.Time
NewTimer(time.Duration) *time.Timer
}

type cage struct {
env *Envars
ecs awsiface.EcsClient
alb awsiface.AlbClient
ec2 awsiface.Ec2Client
env *Envars
ecs awsiface.EcsClient
alb awsiface.AlbClient
ec2 awsiface.Ec2Client
time Time
}

type Input struct {
Env *Envars
ECS awsiface.EcsClient
ALB awsiface.AlbClient
EC2 awsiface.Ec2Client
Env *Envars
ECS awsiface.EcsClient
ALB awsiface.AlbClient
EC2 awsiface.Ec2Client
Time Time
}

func NewCage(input *Input) Cage {
if input.Time == nil {
input.Time = &timeImpl{}
}
return &cage{
env: input.Env,
ecs: input.ECS,
alb: input.ALB,
ec2: input.EC2,
env: input.Env,
ecs: input.ECS,
alb: input.ALB,
ec2: input.EC2,
time: input.Time,
}
}
25 changes: 19 additions & 6 deletions cli/cage/commands/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,32 @@ package commands

import (
"context"
"io"

"github.com/loilo-inc/canarycage/cli/cage/prompt"
"github.com/urfave/cli/v2"
)

type CageCommands interface {
Up() *cli.Command
RollOut() *cli.Command
Run() *cli.Command
Commands() []*cli.Command
}

type cageCommands struct {
ctx context.Context
ctx context.Context
prompt *prompt.Prompter
}

func NewCageCommands(ctx context.Context, stdin io.Reader) CageCommands {
return &cageCommands{ctx: ctx,
prompt: prompt.NewPrompter(stdin),
}
}

func NewCageCommands(ctx context.Context) CageCommands {
return &cageCommands{ctx: ctx}
func (c *cageCommands) Commands() []*cli.Command {
return []*cli.Command{
c.Up(),
c.RollOut(),
c.Run(),
c.Recreate(),
}
}
16 changes: 3 additions & 13 deletions cli/cage/commands/flags.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package commands

import (
"context"
"os"

"github.com/apex/log"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/loilo-inc/canarycage"
cage "github.com/loilo-inc/canarycage"
"github.com/urfave/cli/v2"
)

Expand Down Expand Up @@ -56,21 +55,12 @@ func (c *cageCommands) aggregateEnvars(
ctx *cli.Context,
envars *cage.Envars,
) {
cfg, err := config.LoadDefaultConfig(context.Background())
if err != nil {
log.Fatalf(err.Error())
}

if envars.Region != "" {
log.Infof("🗺 region was set: %s", envars.Region)
}

if cfg.Region != "" {
log.Infof("🗺 region was loaded from default config: %s", cfg.Region)
} else {
log.Fatalf("🙄 region must specified by --region flag or aws session")
}

envars.CI = os.Getenv("CI") == "true"
if ctx.NArg() > 0 {
dir := ctx.Args().Get(0)
td, svc, err := cage.LoadDefinitionsFromFiles(dir)
Expand Down
55 changes: 55 additions & 0 deletions cli/cage/commands/recreate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package commands

import (
"context"

"github.com/apex/log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ecs"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
cage "github.com/loilo-inc/canarycage"
"github.com/urfave/cli/v2"
)

func (c *cageCommands) Recreate() *cli.Command {
envars := cage.Envars{}
return &cli.Command{
Name: "recreate",
Usage: "recreate ECS service with specified service/task definition",
Description: "recreate ECS service with specified service/task definition",
ArgsUsage: "[directory path of service.json and task-definition.json (default=.)]",
Flags: []cli.Flag{
RegionFlag(&envars.Region),
ClusterFlag(&envars.Cluster),
ServiceFlag(&envars.Service),
TaskDefinitionArnFlag(&envars.TaskDefinitionArn),
CanaryTaskIdleDurationFlag(&envars.CanaryTaskIdleDuration),
},
Action: func(ctx *cli.Context) error {
c.aggregateEnvars(ctx, &envars)

if err := c.prompt.ConfirmService(&envars); err != nil {
return err
}
var cfg aws.Config
if o, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(envars.Region)); err != nil {
return err
} else {
cfg = o
}
cagecli := cage.NewCage(&cage.Input{
Env: &envars,
ECS: ecs.NewFromConfig(cfg),
EC2: ec2.NewFromConfig(cfg),
ALB: elbv2.NewFromConfig(cfg),
})
_, err := cagecli.Recreate(context.Background())
if err != nil {
log.Error(err.Error())
}
return err
},
}
}
5 changes: 4 additions & 1 deletion cli/cage/commands/rollout.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ecs"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"github.com/loilo-inc/canarycage"
cage "github.com/loilo-inc/canarycage"
"github.com/urfave/cli/v2"
)

Expand All @@ -35,6 +35,9 @@ func (c *cageCommands) RollOut() *cli.Command {
},
Action: func(ctx *cli.Context) error {
c.aggregateEnvars(ctx, &envars)
if err := c.prompt.ConfirmService(&envars); err != nil {
return err
}
var cfg aws.Config
if o, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(envars.Region)); err != nil {
return err
Expand Down
3 changes: 3 additions & 0 deletions cli/cage/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ func (c *cageCommands) Run() *cli.Command {
},
Action: func(ctx *cli.Context) error {
c.aggregateEnvars(ctx, &envars)
if err := c.prompt.ConfirmTask(&envars); err != nil {
return err
}
var cfg aws.Config
if o, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(envars.Region)); err != nil {
return err
Expand Down
5 changes: 4 additions & 1 deletion cli/cage/commands/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ecs"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"github.com/loilo-inc/canarycage"
cage "github.com/loilo-inc/canarycage"
"github.com/urfave/cli/v2"
)

Expand All @@ -29,6 +29,9 @@ func (c *cageCommands) Up() *cli.Command {
},
Action: func(ctx *cli.Context) error {
c.aggregateEnvars(ctx, &envars)
if err := c.prompt.ConfirmService(&envars); err != nil {
return err
}
var cfg aws.Config
if o, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(envars.Region)); err != nil {
return err
Expand Down
14 changes: 5 additions & 9 deletions cli/cage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"log"
"os"

"github.com/loilo-inc/canarycage/cli/cage/commands"
Expand All @@ -11,18 +12,13 @@ import (
func main() {
app := cli.NewApp()
app.Name = "canarycage"
app.Version = "3.6.0"
app.Version = "3.7.0"
app.Description = "A gradual roll-out deployment tool for AWS ECS"
ctx := context.Background()
cmds := commands.NewCageCommands(ctx)
app.Commands = cli.Commands{
cmds.RollOut(),
cmds.Up(),
cmds.Run(),
}
cmds := commands.NewCageCommands(ctx, os.Stdin)
app.Commands = cmds.Commands()
err := app.Run(os.Args)
if err != nil {
os.Exit(1)
log.Fatal(err)
}
os.Exit(0)
}
75 changes: 75 additions & 0 deletions cli/cage/prompt/prompt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package prompt

import (
"bufio"
"fmt"
"io"
"os"

cage "github.com/loilo-inc/canarycage"
"golang.org/x/xerrors"
)

type Prompter struct {
Reader *bufio.Reader
}

func NewPrompter(stdin io.Reader) *Prompter {
return &Prompter{Reader: bufio.NewReader(stdin)}
}

func (s *Prompter) Confirm(
name string,
value string,
) error {
fmt.Fprintf(os.Stderr, "please confirm [%s]: ", name)
if text, err := s.Reader.ReadString('\n'); err != nil {
return xerrors.Errorf("failed to read from stdin: %w", err)
} else if text[:len(text)-1] != value {
return xerrors.Errorf("%s is not matched. expected: %s", name, value)
}
return nil
}

func (s *Prompter) ConfirmTask(
envars *cage.Envars,
) error {
return s.confirmStackChange(envars, false)
}

func (s *Prompter) ConfirmService(
envars *cage.Envars,
) error {
return s.confirmStackChange(envars, true)
}

func (s *Prompter) confirmStackChange(
envars *cage.Envars,
service bool,
) error {
// Skip confirmation if running in CI
if envars.CI {
return nil
}
if err := s.Confirm("region", envars.Region); err != nil {
return err
}
if err := s.Confirm("cluster", envars.Cluster); err != nil {
return err
}
if service {
if err := s.Confirm("service", envars.Service); err != nil {
return err
}
}
fmt.Fprintf(os.Stderr, "confirm changes:\n")
fmt.Fprintf(os.Stderr, "[region]: %s\n", envars.Region)
fmt.Fprintf(os.Stderr, "[cluster]: %s\n", envars.Cluster)
if service {
fmt.Fprintf(os.Stderr, "[service]: %s\n", envars.Service)
}
if err := s.Confirm("yes", "yes"); err != nil {
return err
}
return nil
}
Loading

0 comments on commit bddefa6

Please sign in to comment.