Skip to content

Commit

Permalink
Split create/update/sync cell commands and refactor cell controller
Browse files Browse the repository at this point in the history
  • Loading branch information
mumoshu committed Nov 21, 2021
1 parent 8720286 commit 72db6f0
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 0 deletions.
145 changes: 145 additions & 0 deletions cmd/okra/create_or_update_cell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package okra

import (
"fmt"
"strconv"
"strings"
"time"

rolloutsv1alpha1 "github.com/mumoshu/okra/api/rollouts/v1alpha1"
okrav1alpha1 "github.com/mumoshu/okra/api/v1alpha1"
"github.com/mumoshu/okra/pkg/cell"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/intstr"
)

func createOrUpdateCellCommand() *cobra.Command {
var input func() *cell.ApplyInput
cmd := &cobra.Command{
Use: "create-or-update-cell",
RunE: func(cmd *cobra.Command, args []string) error {
err := cell.CreateOrUpdate(*input())
return err
},
}
input = initApplyCellFlags(cmd.Flags(), &cell.ApplyInput{})
return cmd
}

func initApplyCellFlags(flag *pflag.FlagSet, c *cell.ApplyInput) func() *cell.ApplyInput {
var (
replicas int
listenerARN string
targetGroupSelector okrav1alpha1.TargetGroupSelector
canarySteps []string
matchLabels []string
)

flag.StringVar(&c.Cell.Namespace, "namespace", "", "Namespace of the target cell")
flag.StringVar(&c.Cell.Name, "name", "", "Name of the target cell")
flag.StringVar(&listenerARN, "listener-arn", "", "ARN of the target AWS Application Load Balancer Listener that is used to receive all the traffic across cluster versions")
flag.StringSliceVar(&matchLabels, "match-label", []string{}, "KVs of labels that is used as target group selector")
flag.StringSliceVar(&targetGroupSelector.VersionLabels, "version-label", []string{okrav1alpha1.DefaultVersionLabelKey}, "Key of the label that is used to indicate the version number of the target group")
flag.IntVar(&replicas, "", 0, "")
flag.StringSliceVar(&canarySteps, "canary-steps", []string{}, "List of canary step definitions. Each step is delimited by a comma(,) and can be one of \"weight=INT\", \"pause=DURATION\", and \"analysis=TEMPLATE:arg1=val1:arg2=val2\"")

return func() *cell.ApplyInput {
spec := c.Cell.Spec.DeepCopy()

if replicas != 0 {
r32 := int32(replicas)
spec.Replicas = &r32
}

targetGroupSelector.MatchLabels = make(map[string]string)
for _, l := range matchLabels {
kv := strings.Split(l, "=")
targetGroupSelector.MatchLabels[kv[0]] = kv[1]
}

var cs []rolloutsv1alpha1.CanaryStep
for _, s := range canarySteps {
var kind, arg string

{
splits := strings.SplitN(s, "=", 2)

if len(splits) != 2 {
panic(fmt.Errorf("pause: unexpected number of args. got %V, wanted only one arg", splits[1:]))
}

kind = splits[0]
arg = splits[1]
}

var step rolloutsv1alpha1.CanaryStep

switch kind {
case "weight":
w, err := strconv.Atoi(arg)
if err != nil {
panic(fmt.Errorf("parsing weight from %s: %w", arg, err))
}

w32 := int32(w)
step.SetWeight = &w32
case "pause":
d, err := time.ParseDuration(arg)
if err != nil {
panic(fmt.Errorf("parsing duration from %s: %w", arg, err))
}

step.Pause = &rolloutsv1alpha1.RolloutPause{
Duration: &intstr.IntOrString{
Type: intstr.String,
StrVal: d.String(),
},
}
case "analysis":
tplAndArgs := strings.Split(arg, ":")
tpl := tplAndArgs[0]

var args []rolloutsv1alpha1.AnalysisRunArgument
for _, a := range tplAndArgs[1:] {
kv := strings.Split(a, "=")

args = append(args, rolloutsv1alpha1.AnalysisRunArgument{
Name: kv[0],
Value: kv[1],
})
}

step.Analysis = &rolloutsv1alpha1.RolloutAnalysis{
Templates: []rolloutsv1alpha1.RolloutAnalysisTemplate{
{
TemplateName: tpl,
},
},
Args: args,
}
default:
panic(fmt.Errorf("unsupported canary step kind: %s", kind))
}

cs = append(cs, step)
}

spec.UpdateStrategy = okrav1alpha1.CellUpdateStrategy{
Type: okrav1alpha1.CellUpdateStrategyTypeCanary,
Canary: &okrav1alpha1.CellUpdateStrategyCanary{
Steps: cs,
},
}

spec.Ingress.AWSApplicationLoadBalancer = &okrav1alpha1.CellIngressAWSApplicationLoadBalancer{
ListenerARN: listenerARN,
TargetGroupSelector: targetGroupSelector,
}

input := c
input.Cell.Spec = *spec

return input
}
}
68 changes: 68 additions & 0 deletions pkg/cell/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cell

import (
"context"

okrav1alpha1 "github.com/mumoshu/okra/api/v1alpha1"
"github.com/mumoshu/okra/pkg/clclient"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

type ApplyInput struct {
Cell okrav1alpha1.Cell

Client client.Client
Scheme *runtime.Scheme
Context context.Context
Log interface {
Info(msg string, keysAndValues ...interface{})
}
}

var noopLogger logger

type logger struct {
}

func (_ logger) Info(msg string, keysAndValues ...interface{}) {

}

func CreateOrUpdate(input ApplyInput) error {
ctx := input.Context
if ctx == nil {
ctx = context.TODO()
}

logger := input.Log
if logger == nil {
logger = noopLogger
}

client, _, err := clclient.Init(input.Client, input.Scheme)
if err != nil {
return err
}

cell := okrav1alpha1.Cell{}
cell.SetNamespace(input.Cell.Namespace)
cell.SetName(input.Cell.Name)

op, err := ctrl.CreateOrUpdate(ctx, client, &cell, func() error {
cell.Spec = input.Cell.Spec

return nil
})
if err != nil {
return err
}

if op != controllerutil.OperationResultNone {
logger.Info("Reconciled Cell", "op", op)
}

return nil
}

0 comments on commit 72db6f0

Please sign in to comment.