pkg/controllers/iamauthpolicy_controller.go (168 lines of code) (raw):

package controllers import ( "context" anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" pkg_aws "github.com/aws/aws-application-networking-k8s/pkg/aws" "github.com/aws/aws-application-networking-k8s/pkg/aws/services" deploy "github.com/aws/aws-application-networking-k8s/pkg/deploy/lattice" "github.com/aws/aws-application-networking-k8s/pkg/k8s" policy "github.com/aws/aws-application-networking-k8s/pkg/k8s/policyhelper" model "github.com/aws/aws-application-networking-k8s/pkg/model/lattice" "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) const ( IAMAuthPolicyAnnotation = "iam-auth-policy" IAMAuthPolicyAnnotationResId = k8s.AnnotationPrefix + IAMAuthPolicyAnnotation + "-resource-id" IAMAuthPolicyAnnotationType = k8s.AnnotationPrefix + IAMAuthPolicyAnnotation + "-resource-type" IAMAuthPolicyFinalizer = k8s.AnnotationPrefix + IAMAuthPolicyAnnotation ) type ( IAP = anv1alpha1.IAMAuthPolicy ) type IAMAuthPolicyController struct { log gwlog.Logger client client.Client pm *deploy.IAMAuthPolicyManager ph *policy.PolicyHandler[*IAP] cloud pkg_aws.Cloud } func RegisterIAMAuthPolicyController(log gwlog.Logger, mgr ctrl.Manager, cloud pkg_aws.Cloud) error { ph := policy.NewIAMAuthPolicyHandler(log, mgr.GetClient()) controller := &IAMAuthPolicyController{ log: log, client: mgr.GetClient(), pm: deploy.NewIAMAuthPolicyManager(cloud), ph: ph, cloud: cloud, } b := ctrl. NewControllerManagedBy(mgr). For(&anv1alpha1.IAMAuthPolicy{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})) ph.AddWatchers(b, &gwv1.Gateway{}, &gwv1.HTTPRoute{}, &gwv1.GRPCRoute{}) err := b.Complete(controller) return err } // Reconciles IAMAuthPolicy CRD. // // IAMAuthPolicy has a plain text policy field and targetRef.Content of policy is not validated by // controller, but Lattice API. // // TargetRef Kind can be Gatbeway, HTTPRoute, or GRPCRoute. Other Kinds will result in Invalid // status. Policy can be attached to single targetRef only. Attempt to attach more than 1 policy // will result in Policy Conflict. If policies created in sequence, the first one will be in // Accepted status, and second in Conflict. Any following updates to accepted policy will put it // into conflicting status, and requires manual resolution - delete conflicting policy. // // Lattice side. Gateway attaches to Lattice ServiceNetwork, and HTTP/GRPCRoute to Service. Policy // attachment changes ServiceNetowrk and Service auth-type to IAM, and detachment to // NONE. Successful creation of lattice policy updates k8s policy annotation with ARN/Id of Lattice // Resouce // // Policy Attachment Spec is defined in [GEP-713]: https://gateway-api.sigs.k8s.io/geps/gep-713/. func (c *IAMAuthPolicyController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { ctx = gwlog.StartReconcileTrace(ctx, c.log, "iamauthpolicy", req.Name, req.Namespace) defer func() { gwlog.EndReconcileTrace(ctx, c.log) }() k8sPolicy := &anv1alpha1.IAMAuthPolicy{} err := c.client.Get(ctx, req.NamespacedName, k8sPolicy) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } c.log.Infow(ctx, "reconcile IAM policy", "req", req, "targetRef", k8sPolicy.Spec.TargetRef) isDelete := !k8sPolicy.DeletionTimestamp.IsZero() var res ctrl.Result if isDelete { res, err = c.reconcileDelete(ctx, k8sPolicy) } else { res, err = c.reconcileUpsert(ctx, k8sPolicy) } if err != nil { return ctrl.Result{}, err } err = c.client.Update(ctx, k8sPolicy) if err != nil { return reconcile.Result{}, err } c.log.Infow(ctx, "reconciled IAM policy", "req", req, "targetRef", k8sPolicy.Spec.TargetRef, "isDeleted", isDelete, ) return res, nil } func (c *IAMAuthPolicyController) reconcileDelete(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) (ctrl.Result, error) { err := c.ph.ValidateTargetRef(ctx, k8sPolicy) if err == nil { modelPolicy := model.NewIAMAuthPolicy(k8sPolicy) _, err := c.pm.Delete(ctx, modelPolicy) if err != nil { return ctrl.Result{}, services.IgnoreNotFound(err) } } err = c.handleLatticeResourceChange(ctx, k8sPolicy, model.IAMAuthPolicyStatus{}) if err != nil { return ctrl.Result{}, err } c.removeFinalizer(k8sPolicy) return ctrl.Result{}, nil } func (c *IAMAuthPolicyController) reconcileUpsert(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) (ctrl.Result, error) { reason, err := c.ph.ValidateAndUpdateCondition(ctx, k8sPolicy) if err != nil { return ctrl.Result{}, err } if reason != policy.ReasonAccepted { return ctrl.Result{}, nil } modelPolicy := model.NewIAMAuthPolicy(k8sPolicy) c.addFinalizer(k8sPolicy) err = c.client.Update(ctx, k8sPolicy) if err != nil { return reconcile.Result{}, err } statusPolicy, err := c.pm.Put(ctx, modelPolicy) if err != nil { return reconcile.Result{}, services.IgnoreNotFound(err) } c.updateLatticeAnnotaion(k8sPolicy, statusPolicy.ResourceId, modelPolicy.Type) err = c.handleLatticeResourceChange(ctx, k8sPolicy, statusPolicy) if err != nil { return reconcile.Result{}, err } return ctrl.Result{}, nil } func (c *IAMAuthPolicyController) removeFinalizer(k8sPolicy *anv1alpha1.IAMAuthPolicy) { if controllerutil.ContainsFinalizer(k8sPolicy, IAMAuthPolicyFinalizer) { controllerutil.RemoveFinalizer(k8sPolicy, IAMAuthPolicyFinalizer) } } func (c *IAMAuthPolicyController) addFinalizer(k8sPolicy *anv1alpha1.IAMAuthPolicy) { if !controllerutil.ContainsFinalizer(k8sPolicy, IAMAuthPolicyFinalizer) { controllerutil.AddFinalizer(k8sPolicy, IAMAuthPolicyFinalizer) } } // cleanup lattice resources after targetRef changes func (c *IAMAuthPolicyController) handleLatticeResourceChange(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy, statusPolicy model.IAMAuthPolicyStatus) error { prevModel, ok := c.getLatticeAnnotation(k8sPolicy) if !ok { return nil } if prevModel.ResourceId != statusPolicy.ResourceId { _, err := c.pm.Delete(ctx, prevModel) if err != nil { return services.IgnoreNotFound(err) } } return nil } func (c *IAMAuthPolicyController) updateLatticeAnnotaion(k8sPolicy *anv1alpha1.IAMAuthPolicy, resId, resType string) { if k8sPolicy.Annotations == nil { k8sPolicy.Annotations = make(map[string]string) } k8sPolicy.Annotations[IAMAuthPolicyAnnotationResId] = resId k8sPolicy.Annotations[IAMAuthPolicyAnnotationType] = resType } func (c *IAMAuthPolicyController) getLatticeAnnotation(k8sPolicy *anv1alpha1.IAMAuthPolicy) (model.IAMAuthPolicy, bool) { if k8sPolicy.Annotations == nil { return model.IAMAuthPolicy{}, false } resourceId := k8sPolicy.Annotations[IAMAuthPolicyAnnotationResId] resourceType := k8sPolicy.Annotations[IAMAuthPolicyAnnotationType] if resourceId == "" || resourceType == "" { return model.IAMAuthPolicy{}, false } return model.IAMAuthPolicy{ Type: resourceType, ResourceId: resourceId, }, true }