pkg/deploy/lattice/rule_synthesizer.go (146 lines of code) (raw):

package lattice import ( "context" "errors" "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-application-networking-k8s/pkg/model/core" model "github.com/aws/aws-application-networking-k8s/pkg/model/lattice" "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" ) type ruleSynthesizer struct { log gwlog.Logger ruleManager RuleManager tgManager TargetGroupManager stack core.Stack } func NewRuleSynthesizer( log gwlog.Logger, ruleManager RuleManager, tgManager TargetGroupManager, stack core.Stack, ) *ruleSynthesizer { return &ruleSynthesizer{ log: log, ruleManager: ruleManager, tgManager: tgManager, stack: stack, } } // helper types for checking which leftover rules are no longer referenced // and need to be deleted type ruleIdMap map[string]*model.Rule type snlKey struct { SvcId string ListenerId string } func (r *ruleSynthesizer) Synthesize(ctx context.Context) error { var resRule []*model.Rule err := r.stack.ListResources(&resRule) if err != nil { return err } // svc id -> listener id -> rule id snlStackRules := make(map[snlKey]ruleIdMap) for _, rule := range resRule { // this will also populate our map with rules for each service+listener err = r.createOrUpdateRules(ctx, rule, snlStackRules) if err != nil { return err } } // for each service/listener, remove any lingering lattice rules err = r.deleteStaleLatticeRules(ctx, snlStackRules) if err != nil { return err } // now we have a clean set of rules, update priorities accordingly err = r.adjustPriorities(ctx, snlStackRules, resRule) if err != nil { return err } return nil } func (r *ruleSynthesizer) createOrUpdateRules(ctx context.Context, rule *model.Rule, snlRules map[snlKey]ruleIdMap) error { stackListener, stackSvc, err := r.getStackObjects(rule) if err != nil { return err } err = r.tgManager.ResolveRuleTgIds(ctx, &rule.Spec.Action, r.stack) if err != nil { return err } status, err := r.ruleManager.Upsert(ctx, rule, stackListener, stackSvc) if err != nil { return fmt.Errorf("failed RuleManager.Upsert due to %s", err) } rule.Status = &status // build a map svc + listener -> all current rules key := snlKey{ SvcId: stackSvc.Status.Id, ListenerId: stackListener.Status.Id, } var ok bool var ruleMap ruleIdMap if ruleMap, ok = snlRules[key]; !ok { // create and add a map if there isn't one already ruleMap = make(ruleIdMap) snlRules[key] = ruleMap } ruleMap[rule.Status.Id] = rule return nil } func (r *ruleSynthesizer) deleteStaleLatticeRules(ctx context.Context, snlRules map[snlKey]ruleIdMap) error { var delErr error for snl := range snlRules { allLatticeRules, err := r.ruleManager.List(ctx, snl.SvcId, snl.ListenerId) if err != nil { return fmt.Errorf("failed RuleManager.List %s/%s, due to %s", snl.SvcId, snl.ListenerId, err) } activeRules := snlRules[snl] for _, lr := range allLatticeRules { if aws.BoolValue(lr.IsDefault) { continue } // if the rule is not in our list of ids, we need to remove it // make sure to skip the default ruleId := aws.StringValue(lr.Id) if _, ok := activeRules[ruleId]; !ok { err := r.ruleManager.Delete(ctx, ruleId, snl.SvcId, snl.ListenerId) if err != nil { delErr = errors.Join(delErr, fmt.Errorf("failed RuleManager.Delete %s/%s/%s, due to %s", snl.SvcId, snl.ListenerId, ruleId, err)) } } } } return delErr } func (r *ruleSynthesizer) adjustPriorities(ctx context.Context, snlStackRules map[snlKey]ruleIdMap, resRule []*model.Rule) error { var updateErr error for snl := range snlStackRules { activeRules := snlStackRules[snl] for _, rule := range activeRules { if rule.Spec.Priority != rule.Status.Priority { // *any* mismatch in priority prompts a batch update of ALL priorities r.log.Debugf(ctx, "Found rule priority mismatch, update required") var rulesToUpdate []*model.Rule for _, snlRule := range activeRules { rulesToUpdate = append(rulesToUpdate, snlRule) } err := r.ruleManager.UpdatePriorities(ctx, snl.SvcId, snl.ListenerId, rulesToUpdate) if err != nil { updateErr = errors.Join(updateErr, fmt.Errorf("failed RuleManager.UpdatePriorities for rules %+v due to %s", resRule, err)) } break } } } return updateErr } func (r *ruleSynthesizer) getStackObjects(rule *model.Rule) (*model.Listener, *model.Service, error) { listener := &model.Listener{} err := r.stack.GetResource(rule.Spec.StackListenerId, listener) if err != nil { return nil, nil, err } svc := &model.Service{} err = r.stack.GetResource(listener.Spec.StackServiceId, svc) if err != nil { return nil, nil, err } return listener, svc, nil } func (r *ruleSynthesizer) PostSynthesize(ctx context.Context) error { return nil }