Skip to content

Commit

Permalink
Added support for creating Access Log Policies
Browse files Browse the repository at this point in the history
  • Loading branch information
Shawn Kaplan committed Oct 11, 2023
1 parent 165be2c commit 7db92d1
Show file tree
Hide file tree
Showing 16 changed files with 1,786 additions and 25 deletions.
58 changes: 50 additions & 8 deletions controllers/accesslogpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
builder2 "sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/predicate"
gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"

anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1"
"github.com/aws/aws-application-networking-k8s/pkg/aws"
"github.com/aws/aws-application-networking-k8s/pkg/aws/services"
"github.com/aws/aws-application-networking-k8s/pkg/config"
"github.com/aws/aws-application-networking-k8s/pkg/deploy"
"github.com/aws/aws-application-networking-k8s/pkg/gateway"
"github.com/aws/aws-application-networking-k8s/pkg/k8s"
lattice_runtime "github.com/aws/aws-application-networking-k8s/pkg/runtime"
"github.com/aws/aws-application-networking-k8s/pkg/utils"
Expand All @@ -50,6 +54,7 @@ type accessLogPolicyReconciler struct {
scheme *runtime.Scheme
finalizerManager k8s.FinalizerManager
eventRecorder record.EventRecorder
modelBuilder gateway.AccessLogSubscriptionModelBuilder
stackDeployer deploy.StackDeployer
cloud aws.Cloud
stackMarshaller deploy.StackMarshaller
Expand All @@ -65,7 +70,8 @@ func RegisterAccessLogPolicyController(
scheme := mgr.GetScheme()
evtRec := mgr.GetEventRecorderFor("accesslogpolicy")

stackDeployer := deploy.NewServiceNetworkStackDeployer(log, cloud, mgrClient)
modelBuilder := gateway.NewAccessLogSubscriptionModelBuilder(log, mgrClient)
stackDeployer := deploy.NewAccessLogSubscriptionStackDeployer(log, cloud, mgrClient)
stackMarshaller := deploy.NewDefaultStackMarshaller()

r := &accessLogPolicyReconciler{
Expand All @@ -74,13 +80,14 @@ func RegisterAccessLogPolicyController(
scheme: scheme,
finalizerManager: finalizerManager,
eventRecorder: evtRec,
modelBuilder: modelBuilder,
stackDeployer: stackDeployer,
cloud: cloud,
stackMarshaller: stackMarshaller,
}

builder := ctrl.NewControllerManagedBy(mgr).
For(&anv1alpha1.AccessLogPolicy{})
For(&anv1alpha1.AccessLogPolicy{}, builder2.WithPredicates(predicate.GenerationChangedPredicate{}))

return builder.Complete(r)
}
Expand Down Expand Up @@ -133,16 +140,24 @@ func (r *accessLogPolicyReconciler) reconcileUpsert(ctx context.Context, alp *an
alp.Spec.TargetRef.Kind, alp.Spec.TargetRef.Name)
err := r.updateAccessLogPolicyStatus(ctx, alp, gwv1alpha2.PolicyReasonTargetNotFound)
if err != nil {
return fmt.Errorf("failed to update Access Log Policy status, %w", err)
return err
}
return nil
}

// TODO: Create VPC Lattice Access Log Subscription
err := r.buildAndDeployModel(ctx, alp)
if err != nil {
if services.IsConflictError(err) {
return r.updateAccessLogPolicyStatus(ctx, alp, gwv1alpha2.PolicyReasonConflicted)
} else if services.IsInvalidError(err) {
return r.updateAccessLogPolicyStatus(ctx, alp, gwv1alpha2.PolicyReasonInvalid)
}
return err
}

err := r.updateAccessLogPolicyStatus(ctx, alp, gwv1alpha2.PolicyReasonAccepted)
err = r.updateAccessLogPolicyStatus(ctx, alp, gwv1alpha2.PolicyReasonAccepted)
if err != nil {
return fmt.Errorf("failed to update Access Log Policy status, %w", err)
return err
}

return nil
Expand All @@ -163,8 +178,8 @@ func (r *accessLogPolicyReconciler) targetRefExists(ctx context.Context, alp *an

switch alp.Spec.TargetRef.Kind {
case "Gateway":
gateway := &gwv1beta1.Gateway{}
err = r.client.Get(ctx, targetRefNamespacedName, gateway)
gw := &gwv1beta1.Gateway{}
err = r.client.Get(ctx, targetRefNamespacedName, gw)
case "HTTPRoute":
httpRoute := &gwv1beta1.HTTPRoute{}
err = r.client.Get(ctx, targetRefNamespacedName, httpRoute)
Expand All @@ -179,6 +194,33 @@ func (r *accessLogPolicyReconciler) targetRefExists(ctx context.Context, alp *an
return err == nil
}

func (r *accessLogPolicyReconciler) buildAndDeployModel(
ctx context.Context,
alp *anv1alpha1.AccessLogPolicy,
) error {
stack, _, err := r.modelBuilder.Build(ctx, alp)
if err != nil {
r.eventRecorder.Event(alp, corev1.EventTypeWarning, k8s.AccessLogPolicyEventReasonFailedBuildModel,
fmt.Sprintf("Failed to build model due to %s", err))
return err
}

jsonStack, err := r.stackMarshaller.Marshal(stack)
if err != nil {
return err
}
r.log.Debugw("Successfully built model", "stack", jsonStack)

if err := r.stackDeployer.Deploy(ctx, stack); err != nil {
return err
}
r.log.Debugw("successfully deployed model",
"stack", stack.StackID().Name+":"+stack.StackID().Namespace,
)

return nil
}

func (r *accessLogPolicyReconciler) updateAccessLogPolicyStatus(
ctx context.Context,
alp *anv1alpha1.AccessLogPolicy,
Expand Down
36 changes: 36 additions & 0 deletions pkg/aws/services/vpclattice.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,42 @@ func IsNotFoundError(err error) bool {
return errors.As(err, &nfErr)
}

type ConflictError struct {
ResourceType string
Name string
Message string
}

func (e *ConflictError) Error() string {
return fmt.Sprintf("%s %s had a conflict: %s", e.ResourceType, e.Name, e.Message)
}

func NewConflictError(resourceType string, name string, message string) error {
return &ConflictError{resourceType, name, message}
}

func IsConflictError(err error) bool {
conflictErr := &ConflictError{}
return errors.As(err, &conflictErr)
}

type InvalidError struct {
Message string
}

func (e *InvalidError) Error() string {
return fmt.Sprintf("Invalid input: %s", e.Message)
}

func NewInvalidError(message string) error {
return &InvalidError{message}
}

func IsInvalidError(err error) bool {
invalidErr := &InvalidError{}
return errors.As(err, &invalidErr)
}

type Lattice interface {
vpclatticeiface.VPCLatticeAPI
ListServiceNetworksAsList(ctx context.Context, input *vpclattice.ListServiceNetworksInput) ([]*vpclattice.ServiceNetworkSummary, error)
Expand Down
106 changes: 106 additions & 0 deletions pkg/deploy/lattice/access_log_subscription_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package lattice

import (
"context"

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

"github.com/aws/aws-application-networking-k8s/pkg/aws"
"github.com/aws/aws-application-networking-k8s/pkg/aws/services"
"github.com/aws/aws-application-networking-k8s/pkg/model/lattice"
"github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog"
)

//go:generate mockgen -destination access_log_subscription_manager_mock.go -package lattice github.com/aws/aws-application-networking-k8s/pkg/deploy/lattice AccessLogSubscriptionManager

type AccessLogSubscriptionManager interface {
Create(ctx context.Context, accessLogSubscription *lattice.AccessLogSubscription) (*lattice.AccessLogSubscriptionStatus, error)
}

type defaultAccessLogSubscriptionManager struct {
log gwlog.Logger
cloud aws.Cloud
}

func NewAccessLogSubscriptionManager(
log gwlog.Logger,
cloud aws.Cloud,
) *defaultAccessLogSubscriptionManager {
return &defaultAccessLogSubscriptionManager{
log: log,
cloud: cloud,
}
}

func (m *defaultAccessLogSubscriptionManager) Create(
ctx context.Context,
accessLogSubscription *lattice.AccessLogSubscription,
) (*lattice.AccessLogSubscriptionStatus, error) {
vpcLatticeSess := m.cloud.Lattice()

var resourceIdentifier string
if accessLogSubscription.Spec.SourceType == lattice.ServiceNetworkSourceType {
serviceNetwork, err := getServiceNetworkWithName(ctx, vpcLatticeSess, accessLogSubscription.Spec.SourceName)
if err != nil {
return nil, err
}
resourceIdentifier = *serviceNetwork.Arn
} else {
service, err := getServiceWithName(ctx, vpcLatticeSess, accessLogSubscription.Spec.SourceName)
if err != nil {
return nil, err
}
resourceIdentifier = *service.Arn
}

createALSInput := &vpclattice.CreateAccessLogSubscriptionInput{
ResourceIdentifier: &resourceIdentifier,
DestinationArn: &accessLogSubscription.Spec.DestinationArn,
}

createALSOutput, err := vpcLatticeSess.CreateAccessLogSubscriptionWithContext(ctx, createALSInput)
if err != nil {
if e, ok := err.(*vpclattice.ConflictException); ok {
return nil, services.NewConflictError(string(accessLogSubscription.Spec.SourceType), accessLogSubscription.Spec.SourceName, e.Message())
} else if e, ok := err.(*vpclattice.AccessDeniedException); ok {
return nil, services.NewInvalidError(e.Message())
} else if e, ok := err.(*vpclattice.ResourceNotFoundException); ok {
if *e.ResourceType == "SERVICE_NETWORK" || *e.ResourceType == "SERVICE" {
return nil, services.NewNotFoundError(string(accessLogSubscription.Spec.SourceType), accessLogSubscription.Spec.SourceName)
}
return nil, services.NewInvalidError(e.Message())
}
return nil, err
}

return &lattice.AccessLogSubscriptionStatus{
Arn: *createALSOutput.Arn,
Id: *createALSOutput.Id,
}, nil
}

func getServiceNetworkWithName(ctx context.Context, vpcLatticeSess services.Lattice, name string) (*vpclattice.ServiceNetworkSummary, error) {
serviceNetworkSummaries, err := vpcLatticeSess.ListServiceNetworksAsList(ctx, &vpclattice.ListServiceNetworksInput{})
if err != nil {
return nil, err
}
for _, serviceNetworkSummary := range serviceNetworkSummaries {
if *serviceNetworkSummary.Name == name {
return serviceNetworkSummary, nil
}
}
return nil, services.NewNotFoundError("ServiceNetwork", name)
}

func getServiceWithName(ctx context.Context, vpcLatticeSess services.Lattice, name string) (*vpclattice.ServiceSummary, error) {
serviceSummaries, err := vpcLatticeSess.ListServicesAsList(ctx, &vpclattice.ListServicesInput{})
if err != nil {
return nil, err
}
for _, serviceSummary := range serviceSummaries {
if *serviceSummary.Name == name {
return serviceSummary, nil
}
}
return nil, services.NewNotFoundError("Service", name)
}
51 changes: 51 additions & 0 deletions pkg/deploy/lattice/access_log_subscription_manager_mock.go

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

Loading

0 comments on commit 7db92d1

Please sign in to comment.