From 9256a327849c118b14743f9a3a130a67852183f0 Mon Sep 17 00:00:00 2001 From: Lucas Caparelli Date: Tue, 10 Oct 2023 19:16:23 -0300 Subject: [PATCH] feat(k8s): support function-associations for user-origins Signed-off-by: Lucas Caparelli --- internal/cloudfront/service.go | 2 +- internal/cloudfront/service_helpers.go | 4 +- internal/k8s/function_association.go | 7 ++ internal/k8s/ingress.go | 38 ++++---- internal/k8s/ingress_fetcher_v1_test.go | 38 ++++---- internal/k8s/ingress_test.go | 93 ++++++------------ internal/k8s/user_origin.go | 91 ++++++++++++++---- internal/k8s/user_origin_test.go | 120 ++++++++++++++++++++---- 8 files changed, 249 insertions(+), 144 deletions(-) diff --git a/internal/cloudfront/service.go b/internal/cloudfront/service.go index 597e625..4bc244f 100644 --- a/internal/cloudfront/service.go +++ b/internal/cloudfront/service.go @@ -140,7 +140,7 @@ func (s *Service) desiredIngresses(ctx context.Context, reconciling k8s.CDNIngre func (s *Service) isPartOfDesiredState(reconciling k8s.CDNIngress) func(k8s.CDNIngress) bool { return func(ing k8s.CDNIngress) bool { isPartOfGroup := ing.Group == reconciling.Group - hasBeenProvisioned := len(ing.LoadBalancerHost) > 0 + hasBeenProvisioned := len(ing.OriginHost) > 0 return !ing.IsBeingRemoved && isPartOfGroup && hasBeenProvisioned } } diff --git a/internal/cloudfront/service_helpers.go b/internal/cloudfront/service_helpers.go index f69619d..cafecf5 100644 --- a/internal/cloudfront/service_helpers.go +++ b/internal/cloudfront/service_helpers.go @@ -35,12 +35,12 @@ func renderDescription(template, group string) string { } func newOrigin(ing k8s.CDNIngress, cfg config.Config, shared k8s.SharedIngressParams) Origin { - builder := NewOriginBuilder(ing.Group, ing.LoadBalancerHost, ing.OriginAccess, cfg). + builder := NewOriginBuilder(ing.Group, ing.OriginHost, ing.OriginAccess, cfg). WithResponseTimeout(ing.OriginRespTimeout). WithRequestPolicy(ing.OriginReqPolicy). WithCachePolicy(ing.CachePolicy) - for _, p := range shared.PathsFromIngress(ing.NamespacedName) { + for _, p := range shared.PathsFromOrigin(ing.OriginHost) { for _, pp := range pathPatternsForPath(p) { builder = builder.WithBehavior(pp, NewFunctions(p.FunctionAssociations)...) } diff --git a/internal/k8s/function_association.go b/internal/k8s/function_association.go index e7d5ded..206d242 100644 --- a/internal/k8s/function_association.go +++ b/internal/k8s/function_association.go @@ -154,6 +154,13 @@ func (fa FunctionAssociations) deepCopy() FunctionAssociations { return copied } +func (fa FunctionAssociations) IsEmpty() bool { + return fa.ViewerRequest == nil && + fa.ViewerResponse == nil && + fa.OriginRequest == nil && + fa.OriginResponse == nil +} + type ViewerFunction struct { ARN string `yaml:"arn"` FunctionType FunctionType `yaml:"functionType"` diff --git a/internal/k8s/ingress.go b/internal/k8s/ingress.go index 7230053..3cd5116 100644 --- a/internal/k8s/ingress.go +++ b/internal/k8s/ingress.go @@ -63,7 +63,7 @@ type Path struct { // CDNIngress represents an Ingress within the bounded context of cdn-origin-controller type CDNIngress struct { types.NamespacedName - LoadBalancerHost string + OriginHost string Group string UnmergedPaths []Path OriginReqPolicy string @@ -95,7 +95,7 @@ var ( // SharedIngressParams represents parameters which might be specified in multiple Ingresses type SharedIngressParams struct { WebACLARN string - paths map[types.NamespacedName][]Path + paths map[string][]Path // map[originHost][]Path } // NewSharedIngressParams creates a new SharedIngressParams from a slice of CDNIngress @@ -116,11 +116,11 @@ func NewSharedIngressParams(ingresses []CDNIngress) (SharedIngressParams, error) }, nil } -func (sp SharedIngressParams) PathsFromIngress(ing types.NamespacedName) []Path { - return sp.paths[ing] +func (sp SharedIngressParams) PathsFromOrigin(originHost string) []Path { + return sp.paths[originHost] } -func mergedPaths(ingresses []CDNIngress) (map[types.NamespacedName][]Path, error) { +func mergedPaths(ingresses []CDNIngress) (map[string][]Path, error) { // Let's put it all in a map of Paths to easily check if we already saw this // Path before and whether to merge it, but we must avoid checking equality of // Paths by also checking Path.FunctionAssociations, because the same Path might @@ -130,17 +130,17 @@ func mergedPaths(ingresses []CDNIngress) (map[types.NamespacedName][]Path, error // instead of just using the input Path, with an empty FunctionAssociations. // This way we ensure we only check equality via PathPattern and PathType. // - // We're also going to create a map[ingressNamespacedName] because this - // struct will later also need to filter paths by Ingress, so it comes in handy + // We're also going to create a map[originHostname] because this + // struct will later also need to filter paths by origin, so it comes in handy // to filter easily. // // The resulting map of all of this is a map of Path indexed by ingress name. - mergedPaths := make(map[types.NamespacedName]map[Path]FunctionAssociations) + mergedPaths := make(map[string]map[Path]FunctionAssociations) for _, ing := range ingresses { - _, ok := mergedPaths[ing.NamespacedName] + _, ok := mergedPaths[ing.OriginHost] if !ok { - mergedPaths[ing.NamespacedName] = make(map[Path]FunctionAssociations) + mergedPaths[ing.OriginHost] = make(map[Path]FunctionAssociations) } for _, p := range ing.UnmergedPaths { @@ -148,9 +148,9 @@ func mergedPaths(ingresses []CDNIngress) (map[types.NamespacedName][]Path, error PathPattern: p.PathPattern, PathType: p.PathType, } - existingFA, ok := mergedPaths[ing.NamespacedName][pKey] + existingFA, ok := mergedPaths[ing.OriginHost][pKey] if !ok { - mergedPaths[ing.NamespacedName][pKey] = p.FunctionAssociations + mergedPaths[ing.OriginHost][pKey] = p.FunctionAssociations continue } @@ -159,23 +159,23 @@ func mergedPaths(ingresses []CDNIngress) (map[types.NamespacedName][]Path, error return nil, fmt.Errorf("conflicting function associations on %q: %v", p.PathPattern, err) } - mergedPaths[ing.NamespacedName][pKey] = mergedFA + mergedPaths[ing.OriginHost][pKey] = mergedFA } } - return mapOfNamespacedNameToPath(mergedPaths), nil + return mapOfOriginHostToPath(mergedPaths), nil } -func mapOfNamespacedNameToPath(ingsPathsAndFAs map[types.NamespacedName]map[Path]FunctionAssociations) map[types.NamespacedName][]Path { - s := make(map[types.NamespacedName][]Path) - for ing, pathsAndFAs := range ingsPathsAndFAs { +func mapOfOriginHostToPath(ingsPathsAndFAs map[string]map[Path]FunctionAssociations) map[string][]Path { + s := make(map[string][]Path) + for originHost, pathsAndFAs := range ingsPathsAndFAs { for p, fa := range pathsAndFAs { path := Path{ PathPattern: p.PathPattern, PathType: p.PathType, FunctionAssociations: fa, } - s[ing] = append(s[ing], path) + s[originHost] = append(s[originHost], path) } } return s @@ -228,7 +228,7 @@ func NewCDNIngressFromV1(ctx context.Context, ing *networkingv1.Ingress, class C } if len(ing.Status.LoadBalancer.Ingress) > 0 { - result.LoadBalancerHost = ing.Status.LoadBalancer.Ingress[0].Hostname + result.OriginHost = ing.Status.LoadBalancer.Ingress[0].Hostname } return result, nil diff --git a/internal/k8s/ingress_fetcher_v1_test.go b/internal/k8s/ingress_fetcher_v1_test.go index dacf7da..d050485 100644 --- a/internal/k8s/ingress_fetcher_v1_test.go +++ b/internal/k8s/ingress_fetcher_v1_test.go @@ -110,11 +110,11 @@ func (s *IngressFetcherV1TestSuite) TestFetchBy_SuccessWithUserOrigins() { - /foo/*`, expectedIngs: []CDNIngress{ { - NamespacedName: types.NamespacedName{Name: "name", Namespace: "namespace"}, - Group: "group", - LoadBalancerHost: "host", - UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, - OriginAccess: "Public", + NamespacedName: types.NamespacedName{Name: "name", Namespace: "namespace"}, + Group: "group", + OriginHost: "host", + UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, + OriginAccess: "Public", }, }, }, @@ -128,11 +128,11 @@ func (s *IngressFetcherV1TestSuite) TestFetchBy_SuccessWithUserOrigins() { originAccess: Bucket`, expectedIngs: []CDNIngress{ { - NamespacedName: types.NamespacedName{Name: "name", Namespace: "namespace"}, - Group: "group", - LoadBalancerHost: "host", - UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, - OriginAccess: "Bucket", + NamespacedName: types.NamespacedName{Name: "name", Namespace: "namespace"}, + Group: "group", + OriginHost: "host", + UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, + OriginAccess: "Bucket", }, }, }, @@ -144,13 +144,12 @@ func (s *IngressFetcherV1TestSuite) TestFetchBy_SuccessWithUserOrigins() { paths: - /foo - /foo/* - viewerFunctionARN: foo originRequestPolicy: None`, expectedIngs: []CDNIngress{ { NamespacedName: types.NamespacedName{Name: "name", Namespace: "namespace"}, Group: "group", - LoadBalancerHost: "host", + OriginHost: "host", UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, OriginRespTimeout: int64(35), OriginReqPolicy: "None", @@ -164,7 +163,6 @@ func (s *IngressFetcherV1TestSuite) TestFetchBy_SuccessWithUserOrigins() { - host: host paths: - /foo - viewerFunctionARN: foo originRequestPolicy: None originAccess: Bucket - host: host @@ -173,17 +171,17 @@ func (s *IngressFetcherV1TestSuite) TestFetchBy_SuccessWithUserOrigins() { - /bar`, expectedIngs: []CDNIngress{ { - NamespacedName: types.NamespacedName{Name: "name", Namespace: "namespace"}, - Group: "group", - LoadBalancerHost: "host", - UnmergedPaths: []Path{{PathPattern: "/foo"}}, - OriginReqPolicy: "None", - OriginAccess: "Bucket", + NamespacedName: types.NamespacedName{Name: "name", Namespace: "namespace"}, + Group: "group", + OriginHost: "host", + UnmergedPaths: []Path{{PathPattern: "/foo"}}, + OriginReqPolicy: "None", + OriginAccess: "Bucket", }, { NamespacedName: types.NamespacedName{Name: "name", Namespace: "namespace"}, Group: "group", - LoadBalancerHost: "host", + OriginHost: "host", UnmergedPaths: []Path{{PathPattern: "/bar"}}, OriginRespTimeout: int64(35), OriginAccess: "Public", diff --git a/internal/k8s/ingress_test.go b/internal/k8s/ingress_test.go index 0a81dbc..837a5f6 100644 --- a/internal/k8s/ingress_test.go +++ b/internal/k8s/ingress_test.go @@ -26,7 +26,6 @@ import ( "github.com/stretchr/testify/suite" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" ) func TestRunCDNIngressTestSuite(t *testing.T) { @@ -181,22 +180,16 @@ func (s *CDNIngressSuite) TestNewCDNIngressFromV1_UsingFunctionAssociationsAndVi s.Empty(got) } -func (s *CDNIngressSuite) Test_sharedIngressParams_SingleIngressIsValid() { +func (s *CDNIngressSuite) Test_sharedIngressParams_SingleOriginIsValid() { params := []CDNIngress{ { - NamespacedName: types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }, + OriginHost: "foo.bar", Group: "foo", UnmergedWebACLARN: "arn:aws:wafv2:us-east-1:000000000000:global/webacl/foo/00000-5c43-4ea0-8424-2ed34dd3434", }, { - NamespacedName: types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }, - Group: "foo", + OriginHost: "foo.bar", + Group: "foo", UnmergedPaths: []Path{ { PathPattern: "/", @@ -215,11 +208,8 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_SingleIngressIsValid() { expected := SharedIngressParams{ WebACLARN: "arn:aws:wafv2:us-east-1:000000000000:global/webacl/foo/00000-5c43-4ea0-8424-2ed34dd3434", - paths: map[types.NamespacedName][]Path{ - types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }: { + paths: map[string][]Path{ + "foo.bar": { { PathPattern: "/", PathType: "Prefix", @@ -235,14 +225,11 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_SingleIngressIsValid() { s.NoError(err) } -func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleIngressesWithDifferentPathsIsValid() { +func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleOriginsWithDifferentPathsIsValid() { params := []CDNIngress{ { - NamespacedName: types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }, - Group: "foo", + OriginHost: "foo.bar", + Group: "foo", UnmergedPaths: []Path{ { PathPattern: "/", @@ -256,11 +243,8 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleIngressesWithDifferen }, }, { - NamespacedName: types.NamespacedName{ - Namespace: "ns2", - Name: "ingress2", - }, - Group: "foo", + OriginHost: "foo.bar2", + Group: "foo", UnmergedPaths: []Path{ { PathPattern: "/foo", @@ -278,11 +262,8 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleIngressesWithDifferen shared, err := NewSharedIngressParams(params) expected := SharedIngressParams{ - paths: map[types.NamespacedName][]Path{ - types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }: { + paths: map[string][]Path{ + "foo.bar": { { PathPattern: "/", PathType: "Prefix", @@ -291,10 +272,7 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleIngressesWithDifferen }, }, }, - types.NamespacedName{ - Namespace: "ns2", - Name: "ingress2", - }: { + "foo.bar2": { { PathPattern: "/foo", PathType: "Prefix", @@ -310,14 +288,11 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleIngressesWithDifferen s.NoError(err) } -func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleIngressesWithSamePathButDifferentFunctionEventTypesIsValid() { +func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleOriginsWithSamePathButDifferentFunctionEventTypesIsValid() { params := []CDNIngress{ { - NamespacedName: types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }, - Group: "foo", + OriginHost: "foo.bar", + Group: "foo", UnmergedPaths: []Path{ { PathPattern: "/", @@ -331,11 +306,8 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleIngressesWithSamePath }, }, { - NamespacedName: types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }, - Group: "foo", + OriginHost: "foo.bar", + Group: "foo", UnmergedPaths: []Path{ { PathPattern: "/", @@ -353,11 +325,8 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_MultipleIngressesWithSamePath shared, err := NewSharedIngressParams(params) expected := SharedIngressParams{ - paths: map[types.NamespacedName][]Path{ - types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }: { + paths: map[string][]Path{ + "foo.bar": { { PathPattern: "/", PathType: "Prefix", @@ -501,13 +470,10 @@ func (s *CDNIngressSuite) Test_sharedIngressParams_ConflictingWebACLs() { s.ErrorIs(err, errSharedParamsConflictingACL) } -func (s *CDNIngressSuite) TestSharedIngressParams_PathsFromIngress() { +func (s *CDNIngressSuite) TestSharedIngressParams_PathsFromOrigin() { shared := SharedIngressParams{ - paths: map[types.NamespacedName][]Path{ - types.NamespacedName{ - Namespace: "ns1", - Name: "ingress1", - }: { + paths: map[string][]Path{ + "foo.bar": { { PathPattern: "/", PathType: "Prefix", @@ -519,10 +485,7 @@ func (s *CDNIngressSuite) TestSharedIngressParams_PathsFromIngress() { }, }, }, - types.NamespacedName{ - Namespace: "ns2", - Name: "ingress2", - }: { + "foo.bar2": { { PathPattern: "/foo", PathType: "Prefix", @@ -547,7 +510,7 @@ func (s *CDNIngressSuite) TestSharedIngressParams_PathsFromIngress() { }, } - s.Empty(shared.PathsFromIngress(types.NamespacedName{Name: "I don't exist"})) + s.Empty(shared.PathsFromOrigin("I don't exist")) s.ElementsMatch([]Path{ { @@ -560,7 +523,7 @@ func (s *CDNIngressSuite) TestSharedIngressParams_PathsFromIngress() { }, }, }, - }, shared.PathsFromIngress(types.NamespacedName{Namespace: "ns1", Name: "ingress1"})) + }, shared.PathsFromOrigin("foo.bar")) s.ElementsMatch([]Path{ { @@ -582,5 +545,5 @@ func (s *CDNIngressSuite) TestSharedIngressParams_PathsFromIngress() { ARN: "some-other-arn3", }, }, - }}, shared.PathsFromIngress(types.NamespacedName{Namespace: "ns2", Name: "ingress2"})) + }}, shared.PathsFromOrigin("foo.bar2")) } diff --git a/internal/k8s/user_origin.go b/internal/k8s/user_origin.go index e6b0543..706bc49 100644 --- a/internal/k8s/user_origin.go +++ b/internal/k8s/user_origin.go @@ -20,12 +20,15 @@ package k8s import ( + "errors" "fmt" "github.com/creasty/defaults" "gopkg.in/yaml.v3" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/Gympass/cdn-origin-controller/internal/strhelper" ) const ( @@ -51,7 +54,7 @@ func cdnIngressesForUserOrigins(obj client.Object) ([]CDNIngress, error) { for _, o := range origins { ing := CDNIngress{ NamespacedName: types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}, - LoadBalancerHost: o.Host, + OriginHost: o.Host, Group: groupAnnotationValue(obj), UnmergedPaths: o.paths(), OriginReqPolicy: o.RequestPolicy, @@ -66,36 +69,84 @@ func cdnIngressesForUserOrigins(obj client.Object) ([]CDNIngress, error) { return result, nil } -// TODO in upcoming PRs: -// parse and validate function associations, ensuring: -// a. it only references paths present in this custom origin -// b. do not break any existing rules that are already mapped in fa.Validate() - type userOrigin struct { - Host string `yaml:"host"` - ResponseTimeout int64 `yaml:"responseTimeout"` - Paths []string `yaml:"paths"` - ViewerFunctionARN string `yaml:"viewerFunctionARN"` - RequestPolicy string `yaml:"originRequestPolicy"` - CachePolicy string `yaml:"cachePolicy"` - WebACLARN string `yaml:"webACLARN"` - OriginAccess string `yaml:"originAccess" default:"Public"` + Host string `yaml:"host"` + ResponseTimeout int64 `yaml:"responseTimeout"` + Paths []string `yaml:"paths"` // deprecated in favor of Behaviors + Behaviors []customOriginBehavior `yaml:"behaviors"` + ViewerFunctionARN string `yaml:"viewerFunctionARN"` // deprecated in favor of Behaviors + RequestPolicy string `yaml:"originRequestPolicy"` + CachePolicy string `yaml:"cachePolicy"` + WebACLARN string `yaml:"webACLARN"` + OriginAccess string `yaml:"originAccess" default:"Public"` +} + +type customOriginBehavior struct { + Path string `yaml:"path"` + FunctionAssociations FunctionAssociations `yaml:"functionAssociations"` } func (o userOrigin) paths() []Path { var paths []Path for _, p := range o.Paths { - paths = append(paths, Path{PathPattern: p}) + path := Path{PathPattern: p} + if len(o.ViewerFunctionARN) > 0 { + path.FunctionAssociations = newFAFromViewerFunctionARN(o.ViewerFunctionARN) + } + paths = append(paths, path) + } + + for _, b := range o.Behaviors { + paths = append(paths, Path{ + PathPattern: b.Path, + FunctionAssociations: b.FunctionAssociations, + }) } + return paths } -func (o userOrigin) isValid() bool { - return len(o.Host) > 0 && len(o.Paths) > 0 && (o.OriginAccess == CFUserOriginAccessPublic || o.OriginAccess == CFUserOriginAccessBucket) +func (o userOrigin) validate() error { + if err := o.validateBehaviors(); err != nil { + return err + } + + if len(o.Host) == 0 { + return errors.New("the origin must have a host") + } + + if len(o.Paths) == 0 && len(o.Behaviors) == 0 { + return errors.New("the origin must have at least one path or behavior") + } + + if o.OriginAccess != CFUserOriginAccessPublic && o.OriginAccess != CFUserOriginAccessBucket { + return fmt.Errorf("the origin must specify a valid originAccess. Valid values: %q, %q", + CFUserOriginAccessPublic, CFUserOriginAccessBucket) + } + + return nil +} + +func (o userOrigin) validateBehaviors() error { + for _, b := range o.Behaviors { + if err := b.FunctionAssociations.Validate(); err != nil { + return fmt.Errorf("validating behavior function associations: %v", err) + } + + if strhelper.Contains(o.Paths, b.Path) { + return fmt.Errorf("same path %q informed in paths (deprecated) and behaviors. Specify it in behaviors only", b.Path) + } + + if !b.FunctionAssociations.IsEmpty() && len(o.ViewerFunctionARN) > 0 { + return errors.New("function associations declared in behaviors, but viewerFunctionArn is present (deprecated). " + + "Configure all functions via behaviors only") + } + } + return nil } func userOriginsFromYAML(originsData []byte) ([]userOrigin, error) { - origins := []userOrigin{} + var origins []userOrigin err := yaml.Unmarshal(originsData, &origins) if err != nil { return nil, err @@ -108,8 +159,8 @@ func userOriginsFromYAML(originsData []byte) ([]userOrigin, error) { return nil, err } - if !o.isValid() { - return nil, fmt.Errorf("user origin invalid. Must have at lease one path, must have a host, and origin access must be Public or Bucket: has %d paths and the host is %q", len(o.Paths), o.Host) + if err := o.validate(); err != nil { + return nil, fmt.Errorf("validating user origin: %v", err) } origins[i] = o diff --git a/internal/k8s/user_origin_test.go b/internal/k8s/user_origin_test.go index 52a97cc..0bfcfc2 100644 --- a/internal/k8s/user_origin_test.go +++ b/internal/k8s/user_origin_test.go @@ -55,10 +55,10 @@ func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_Success() { - /foo/*`, expectedIngs: []CDNIngress{ { - Group: "group", - LoadBalancerHost: "foo.com", - UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, - OriginAccess: "Public", + Group: "group", + OriginHost: "foo.com", + UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, + OriginAccess: "Public", }, }, }, @@ -72,10 +72,10 @@ func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_Success() { originAccess: Bucket`, expectedIngs: []CDNIngress{ { - Group: "group", - LoadBalancerHost: "foo.com", - UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, - OriginAccess: "Bucket", + Group: "group", + OriginHost: "foo.com", + UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, + OriginAccess: "Bucket", }, }, }, @@ -87,12 +87,11 @@ func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_Success() { paths: - /foo - /foo/* - viewerFunctionARN: foo originRequestPolicy: None`, expectedIngs: []CDNIngress{ { Group: "group", - LoadBalancerHost: "foo.com", + OriginHost: "foo.com", UnmergedPaths: []Path{{PathPattern: "/foo"}, {PathPattern: "/foo/*"}}, OriginRespTimeout: int64(35), OriginReqPolicy: "None", @@ -106,7 +105,6 @@ func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_Success() { - host: foo.com paths: - /foo - viewerFunctionARN: foo originRequestPolicy: None originAccess: Bucket - host: bar.com @@ -115,15 +113,15 @@ func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_Success() { - /bar`, expectedIngs: []CDNIngress{ { - Group: "group", - LoadBalancerHost: "foo.com", - UnmergedPaths: []Path{{PathPattern: "/foo"}}, - OriginReqPolicy: "None", - OriginAccess: "Bucket", + Group: "group", + OriginHost: "foo.com", + UnmergedPaths: []Path{{PathPattern: "/foo"}}, + OriginReqPolicy: "None", + OriginAccess: "Bucket", }, { Group: "group", - LoadBalancerHost: "bar.com", + OriginHost: "bar.com", UnmergedPaths: []Path{{PathPattern: "/bar"}}, OriginRespTimeout: int64(35), OriginAccess: "Public", @@ -147,6 +145,94 @@ func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_Success() { } } +func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_WithViewerFunctionARNIsValid() { + userOriginsYAML := ` +- host: foo.com + viewerFunctionARN: some-arn + paths: + - /foo + - /foo/* +` + ing := &networkingv1.Ingress{} + ing.Annotations = map[string]string{ + cfUserOriginsAnnotation: userOriginsYAML, + CDNGroupAnnotation: "group", + } + + got, err := cdnIngressesForUserOrigins(ing) + s.NoError(err) + + s.Equal(&ViewerRequestFunction{ + ViewerFunction: ViewerFunction{ + ARN: "some-arn", + FunctionType: FunctionTypeCloudfront, + }, + IncludeBody: false, + }, got[0].UnmergedPaths[0].FunctionAssociations.ViewerRequest) + + s.Equal(&ViewerRequestFunction{ + ViewerFunction: ViewerFunction{ + ARN: "some-arn", + FunctionType: FunctionTypeCloudfront, + }, + IncludeBody: false, + }, got[0].UnmergedPaths[1].FunctionAssociations.ViewerRequest) +} + +func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_WithBehaviorsIsValid() { + userOriginsYAML := ` +- host: foo.com + behaviors: + - path: /foo + functionAssociations: + viewerRequest: + arn: arn:aws:cloudfront::000000000000:function/test-function-associations + functionType: cloudfront + viewerResponse: + arn: arn:aws:cloudfront::000000000000:function/test-function-associations + functionType: cloudfront + originRequest: + arn: arn:aws:lambda:us-east-1:000000000000:function:test-function-associations + includeBody: true + originResponse: + arn: arn:aws:lambda:us-east-1:000000000000:function:test-function-associations +` + ing := &networkingv1.Ingress{} + ing.Annotations = map[string]string{ + cfUserOriginsAnnotation: userOriginsYAML, + CDNGroupAnnotation: "group", + } + + got, err := cdnIngressesForUserOrigins(ing) + s.NoError(err) + + s.Len(got, 1) + s.Len(got[0].UnmergedPaths, 1) + + s.Equal("/foo", got[0].UnmergedPaths[0].PathPattern) + s.Equal(FunctionAssociations{ + ViewerRequest: &ViewerRequestFunction{ + ViewerFunction: ViewerFunction{ + ARN: "arn:aws:cloudfront::000000000000:function/test-function-associations", + FunctionType: FunctionTypeCloudfront, + }, + }, + ViewerResponse: &ViewerFunction{ + ARN: "arn:aws:cloudfront::000000000000:function/test-function-associations", + FunctionType: FunctionTypeCloudfront, + }, + OriginRequest: &OriginRequestFunction{ + OriginFunction: OriginFunction{ + ARN: "arn:aws:lambda:us-east-1:000000000000:function:test-function-associations", + }, + IncludeBody: true, + }, + OriginResponse: &OriginFunction{ + ARN: "arn:aws:lambda:us-east-1:000000000000:function:test-function-associations", + }, + }, got[0].UnmergedPaths[0].FunctionAssociations) +} + func (s *userOriginSuite) Test_cdnIngressesForUserOrigins_InvalidAnnotationValue() { testCases := []struct { name string