Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/aws-application-networking-k8s/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func main() {
setupLog.Fatalf("accesslogpolicy controller setup failed: %s", err)
}

err = controllers.RegisterIAMAuthPolicyController(ctrlLog.Named("iam-auth-policy"), mgr)
err = controllers.RegisterIAMAuthPolicyController(ctrlLog.Named("iam-auth-policy"), mgr, cloud)
if err != nil {
setupLog.Fatalf("iam auth policy controller setup failed: %s", err)
}
Expand Down
188 changes: 159 additions & 29 deletions controllers/iamauthpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,32 @@ package controllers

import (
"context"
"fmt"
"time"

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"
model "github.com/aws/aws-application-networking-k8s/pkg/model/lattice"
"github.com/aws/aws-application-networking-k8s/pkg/utils"
"github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog"

k8serr "k8s.io/apimachinery/pkg/api/errors"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

type IAMAuthPolicyController struct {
log gwlog.Logger
client client.Client
log gwlog.Logger
client client.Client
policyMgr deploy.IAMAuthPolicyManager
}

func RegisterIAMAuthPolicyController(log gwlog.Logger, mgr ctrl.Manager) error {
func RegisterIAMAuthPolicyController(log gwlog.Logger, mgr ctrl.Manager, cloud pkg_aws.Cloud) error {
controller := &IAMAuthPolicyController{
log: log,
client: mgr.GetClient(),
log: log,
client: mgr.GetClient(),
policyMgr: deploy.IAMAuthPolicyManager{Cloud: cloud},
}
err := ctrl.NewControllerManagedBy(mgr).
For(&anv1alpha1.IAMAuthPolicy{}).
Expand All @@ -29,40 +36,163 @@ func RegisterIAMAuthPolicyController(log gwlog.Logger, mgr ctrl.Manager) error {
}

func (c *IAMAuthPolicyController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
c.log.Infow("reconcile", "req", req)

policy := &anv1alpha1.IAMAuthPolicy{}
err := c.client.Get(ctx, req.NamespacedName, policy)
if !k8serr.IsNotFound(err) {
return ctrl.Result{}, err
k8sPolicy := &anv1alpha1.IAMAuthPolicy{}
err := c.client.Get(ctx, req.NamespacedName, k8sPolicy)
if err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
c.log.Infow("reconcile", "req", req, "targetRef", k8sPolicy.Spec.TargetRef)

c.handleFinalizer(ctx, k8sPolicy)

switch policy.Spec.TargetRef.Kind {
isDelete := !k8sPolicy.DeletionTimestamp.IsZero()
kind := k8sPolicy.Spec.TargetRef.Kind
var reconcileFunc func(context.Context, *anv1alpha1.IAMAuthPolicy) (string, error)
switch kind {
case "Gateway":
err = c.reconcileGateway(ctx, policy)
break
case "HTTPRoute":
case "GRPCRoute":
err = c.reconcileRoute(ctx, policy)
break
if isDelete {
reconcileFunc = c.deleteGatewayPolicy
} else {
reconcileFunc = c.upsertGatewayPolicy
}
case "HTTPRoute", "GRPCRoute":
if isDelete {
reconcileFunc = c.deleteRoutePolicy
} else {
reconcileFunc = c.upsertRoutePolicy
}
default:
err = fmt.Errorf("unsupported targetRef type, req=%s, kind=%s",
req, policy.Spec.TargetRef.Kind)
c.log.Errorw("unsupported targetRef", "kind", kind, "req", req)
return ctrl.Result{RequeueAfter: time.Hour}, nil
}

latticeResourceId, err := reconcileFunc(ctx, k8sPolicy)
if err != nil {
if services.IsNotFoundError(err) {
c.log.Infof("reconcile error, retry in 30sec: %s", err)
return ctrl.Result{RequeueAfter: time.Second * 30}, nil
}
return ctrl.Result{}, err
}

k8sPolicy.Annotations["application-networking.k8s.aws/resourceId"] = latticeResourceId

err = c.client.Update(ctx, k8sPolicy)
if err != nil {
return ctrl.Result{}, err
}

c.log.Infow("successfully reconciled", "req", req)
c.log.Infow("reconciled IAM policy",
"req", req,
"targetRef", k8sPolicy.Spec.TargetRef,
"latticeResorceId", latticeResourceId,
"isDeleted", isDelete,
)
return ctrl.Result{}, nil
}

func (c *IAMAuthPolicyController) reconcileGateway(ctx context.Context, policy *anv1alpha1.IAMAuthPolicy) error {
c.log.Debugw("reconcile gateway iam policy", "policy", policy)
return nil
func (c *IAMAuthPolicyController) handleFinalizer(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) {
authPolicyFinalizer := "iamauthpolicy.k8s.aws/resources"
if k8sPolicy.DeletionTimestamp.IsZero() {
if !controllerutil.ContainsFinalizer(k8sPolicy, authPolicyFinalizer) {
controllerutil.AddFinalizer(k8sPolicy, authPolicyFinalizer)
}
} else {
if controllerutil.ContainsFinalizer(k8sPolicy, authPolicyFinalizer) {
controllerutil.RemoveFinalizer(k8sPolicy, authPolicyFinalizer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When handling resouce deletion, we should do RemoveFinalizer() at very last, to avoid actual reconcile logic return err and k8s api server lose track of that IAMAuthPolicy resource?

For example, in our other controller:

if err := r.cleanupRouteResources(ctx, route); err != nil {
return fmt.Errorf("failed to cleanup GRPCRoute %s, %s: %w", route.Name(), route.Namespace(), err)
}
if err := updateRouteListenerStatus(ctx, r.client, route); err != nil {
return err
}
r.log.Infow("reconciled", "name", req.Name)
return r.finalizerManager.RemoveFinalizers(ctx, route.K8sObject(), routeTypeToFinalizer[r.routeType])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RemoveFinalizer() only mutates object, but does not call kube-api. Need to do client.Update/Patch to apply changes.
In my case I do update at the very last step in Reconcile function, so until then finalizer is not added or removed

}
}
}

func (c *IAMAuthPolicyController) deleteGatewayPolicy(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) (string, error) {
snId, err := c.findSnId(ctx, k8sPolicy)
if err != nil {
return "", err
}
err = c.policyMgr.Delete(ctx, snId)
if err != nil {
return "", err
}
err = c.policyMgr.DisableSnIAMAuth(ctx, snId)
if err != nil {
return "", err
}
return snId, nil
}

func (c *IAMAuthPolicyController) findSnId(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) (string, error) {
tr := k8sPolicy.Spec.TargetRef
snInfo, err := c.policyMgr.Cloud.Lattice().FindServiceNetworkByK8sName(ctx, string(tr.Name))
if err != nil {
return "", err
}
return *snInfo.SvcNetwork.Id, nil
}

func (c *IAMAuthPolicyController) upsertGatewayPolicy(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) (string, error) {
snId, err := c.findSnId(ctx, k8sPolicy)
if err != nil {
return "", err
}
err = c.policyMgr.EnableSnIAMAuth(ctx, snId)
if err != nil {
return "", err
}
err = c.putPolicy(ctx, snId, k8sPolicy.Spec.Policy)
if err != nil {
return "", err
}

return snId, nil
}

func (c *IAMAuthPolicyController) findSvcId(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) (string, error) {
tr := k8sPolicy.Spec.TargetRef
svcName := utils.LatticeServiceName(string(tr.Name), k8sPolicy.Namespace)
svcInfo, err := c.policyMgr.Cloud.Lattice().FindServiceByK8sName(ctx, svcName)
if err != nil {
return "", err
}
return *svcInfo.Id, nil
}

func (c IAMAuthPolicyController) reconcileRoute(ctx context.Context, policy *anv1alpha1.IAMAuthPolicy) error {
c.log.Debugw("reconcile route iam policy", "policy", policy)
return nil
func (c *IAMAuthPolicyController) deleteRoutePolicy(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) (string, error) {
svcId, err := c.findSvcId(ctx, k8sPolicy)
if err != nil {
return "", err
}
err = c.policyMgr.Delete(ctx, svcId)
if err != nil {
return "", err
}
err = c.policyMgr.DisableSvcIAMAuth(ctx, svcId)
if err != nil {
return "", err
}
return svcId, nil
}

func (c *IAMAuthPolicyController) upsertRoutePolicy(ctx context.Context, k8sPolicy *anv1alpha1.IAMAuthPolicy) (string, error) {
svcId, err := c.findSvcId(ctx, k8sPolicy)
if err != nil {
return "", err
}
err = c.policyMgr.EnableSvcIAMAuth(ctx, svcId)
if err != nil {
return "", err
}
err = c.putPolicy(ctx, svcId, k8sPolicy.Spec.Policy)
if err != nil {
return "", err
}
return svcId, nil
}

func (c *IAMAuthPolicyController) putPolicy(ctx context.Context, resId, policy string) error {
modelPolicy := model.IAMAuthPolicy{
ResourceId: resId,
Policy: policy,
}
_, err := c.policyMgr.Put(ctx, modelPolicy)
return err
}
10 changes: 10 additions & 0 deletions pkg/aws/services/vpclattice.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ type Lattice interface {
ListServiceNetworkServiceAssociationsAsList(ctx context.Context, input *vpclattice.ListServiceNetworkServiceAssociationsInput) ([]*vpclattice.ServiceNetworkServiceAssociationSummary, error)
FindServiceNetwork(ctx context.Context, name string, accountId string) (*ServiceNetworkInfo, error)
FindService(ctx context.Context, nameProvider LatticeServiceNameProvider) (*vpclattice.ServiceSummary, error)
FindServiceByK8sName(ctx context.Context, k8sname string) (*vpclattice.ServiceSummary, error)
FindServiceNetworkByK8sName(ctx context.Context, k8sname string) (*ServiceNetworkInfo, error)
}

type defaultLattice struct {
Expand Down Expand Up @@ -315,3 +317,11 @@ func accountIdMatches(accountId string, itemArn string) (bool, error) {

return accountId == parsedArn.AccountID, nil
}

func (d *defaultLattice) FindServiceByK8sName(ctx context.Context, k8sname string) (*vpclattice.ServiceSummary, error) {
return d.FindService(ctx, NewDefaultLatticeServiceNameProvider(k8sname))
}

func (d *defaultLattice) FindServiceNetworkByK8sName(ctx context.Context, k8sname string) (*ServiceNetworkInfo, error) {
return d.FindServiceNetwork(ctx, k8sname, "")
}
30 changes: 30 additions & 0 deletions pkg/aws/services/vpclattice_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 73 additions & 0 deletions pkg/deploy/lattice/iamauthpolicy_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package lattice

import (
"context"

pkg_aws "github.com/aws/aws-application-networking-k8s/pkg/aws"
model "github.com/aws/aws-application-networking-k8s/pkg/model/lattice"

"github.com/aws/aws-sdk-go/service/vpclattice"
)

type IAMAuthPolicyManager struct {
Cloud pkg_aws.Cloud
}

func (m *IAMAuthPolicyManager) Put(ctx context.Context, policy model.IAMAuthPolicy) (model.IAMAuthPolicyStatus, error) {
req := &vpclattice.PutAuthPolicyInput{
Policy: &policy.Policy,
ResourceIdentifier: &policy.ResourceId,
}
resp, err := m.Cloud.Lattice().PutAuthPolicyWithContext(ctx, req)
if err != nil {
return model.IAMAuthPolicyStatus{}, err
}
return model.IAMAuthPolicyStatus{
ResourceId: policy.ResourceId,
State: *resp.State,
}, nil
}

func (m *IAMAuthPolicyManager) Delete(ctx context.Context, resourceId string) error {
req := &vpclattice.DeleteAuthPolicyInput{
ResourceIdentifier: &resourceId,
}
_, err := m.Cloud.Lattice().DeleteAuthPolicyWithContext(ctx, req)
if err != nil {
return err
}
return nil
}

func (m *IAMAuthPolicyManager) EnableSnIAMAuth(ctx context.Context, snId string) error {
return m.setSnAuthType(ctx, snId, vpclattice.AuthTypeAwsIam)
}
func (m *IAMAuthPolicyManager) DisableSnIAMAuth(ctx context.Context, snId string) error {
return m.setSnAuthType(ctx, snId, vpclattice.AuthTypeNone)
}

func (m *IAMAuthPolicyManager) setSnAuthType(ctx context.Context, snId, authType string) error {
req := &vpclattice.UpdateServiceNetworkInput{
AuthType: &authType,
ServiceNetworkIdentifier: &snId,
}
_, err := m.Cloud.Lattice().UpdateServiceNetworkWithContext(ctx, req)
return err
}

func (m *IAMAuthPolicyManager) EnableSvcIAMAuth(ctx context.Context, svcId string) error {
return m.setSvcAuthType(ctx, svcId, vpclattice.AuthTypeAwsIam)
}

func (m *IAMAuthPolicyManager) DisableSvcIAMAuth(ctx context.Context, svcId string) error {
return m.setSvcAuthType(ctx, svcId, vpclattice.AuthTypeNone)
}

func (m *IAMAuthPolicyManager) setSvcAuthType(ctx context.Context, svcId, authType string) error {
req := &vpclattice.UpdateServiceInput{
AuthType: &authType,
ServiceIdentifier: &svcId,
}
_, err := m.Cloud.Lattice().UpdateServiceWithContext(ctx, req)
return err
}
Loading