Skip to content

Commit

Permalink
Cdn behaviors (#899)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhsinger-klotho authored Jan 26, 2024
1 parent 4ee2877 commit 84216dc
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 30 deletions.
20 changes: 19 additions & 1 deletion pkg/engine2/solution_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package engine2

import (
"errors"
"fmt"

construct "github.com/klothoplatform/klotho/pkg/construct2"
"github.com/klothoplatform/klotho/pkg/engine2/constraints"
Expand Down Expand Up @@ -90,9 +91,26 @@ func (ctx solutionContext) LoadGraph(graph construct.Graph) error {
if err := op.AddVerticesFrom(graph); err != nil {
return err
}
if err := raw.AddEdgesFrom(graph); err != nil {

edges, err := graph.Edges()
if err != nil {
return err
}
for _, edge := range edges {
edgeTemplate := ctx.KB.GetEdgeTemplate(edge.Source, edge.Target)
if edgeTemplate == nil {
return fmt.Errorf("edge template %s -> %s not found", edge.Source, edge.Target)
}
if edgeTemplate.AlwaysProcess {
if err := op.AddEdge(edge.Source, edge.Target); err != nil {
return err
}
} else {
if err := raw.AddEdge(edge.Source, edge.Target); err != nil {
return err
}
}
}

// ensure any deployment dependencies due to properties are in place
return construct.WalkGraph(ctx.RawView(), func(id construct.ResourceId, resource *construct.Resource, nerr error) error {
Expand Down
4 changes: 3 additions & 1 deletion pkg/engine2/testdata/cf_distribution.expect.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
resources:
aws:cloudfront_distribution:cloudfront_distribution_2:
CloudfrontDefaultCertificate: true
ViewerCertificate:
CloudfrontDefaultCertificate: true
CacheBehaviors: []
DefaultCacheBehavior:
AllowedMethods:
- DELETE
Expand Down
8 changes: 8 additions & 0 deletions pkg/engine2/testdata/same_path_multiple_methods.expect.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ resources:
Triggers:
rest_api_0_integration_0: rest_api_0_integration_0
rest_api_0_integration_0_method: rest_api_0_integration_0_method
rest_api_0_integration_1: rest_api_0_integration_1
rest_api_0_integration_1_method: rest_api_0_integration_1_method
rest_api_0_integration_2: rest_api_0_integration_2
rest_api_0_integration_2_method: rest_api_0_integration_2_method
aws:rest_api:rest_api_0:
BinaryMediaTypes:
- application/octet-stream
Expand Down Expand Up @@ -64,7 +68,11 @@ edges:
aws:api_stage:rest_api_0:api_stage-0 -> aws:api_deployment:rest_api_0:api_deployment-0:
aws:api_stage:rest_api_0:api_stage-0 -> aws:rest_api:rest_api_0:
aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_1:
aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_2:
aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_method:rest_api_0:rest_api_0_integration_1_method:
aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_method:rest_api_0:rest_api_0_integration_2_method:
aws:api_deployment:rest_api_0:api_deployment-0 -> aws:rest_api:rest_api_0:
aws:rest_api:rest_api_0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
aws:rest_api:rest_api_0 -> aws:api_integration:rest_api_0:rest_api_0_integration_1:
Expand Down
44 changes: 24 additions & 20 deletions pkg/engine2/testdata/same_path_multiple_methods.iac-viz.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,46 @@ resources:
aws:api_resource:rest_api_0/api_resource-1:

aws:api_resource:rest_api_0/api_resource-1 -> rest_api/rest_api_0:
aws:api_resource:rest_api_0/api_resource-2:

aws:api_resource:rest_api_0/api_resource-2 -> rest_api/rest_api_0:
aws:api_method:rest_api_0/rest_api_0_integration_0_method:

aws:api_method:rest_api_0/rest_api_0_integration_0_method -> aws:api_resource:rest_api_0/api_resource-1:
aws:api_method:rest_api_0/rest_api_0_integration_0_method -> rest_api/rest_api_0:
aws:api_method:rest_api_0/rest_api_0_integration_1_method:

aws:api_method:rest_api_0/rest_api_0_integration_1_method -> aws:api_resource:rest_api_0/api_resource-2:
aws:api_method:rest_api_0/rest_api_0_integration_1_method -> rest_api/rest_api_0:
aws:api_method:rest_api_0/rest_api_0_integration_2_method:

aws:api_method:rest_api_0/rest_api_0_integration_2_method -> aws:api_resource:rest_api_0/api_resource-2:
aws:api_method:rest_api_0/rest_api_0_integration_2_method -> rest_api/rest_api_0:
aws:api_integration:rest_api_0/rest_api_0_integration_0:

aws:api_integration:rest_api_0/rest_api_0_integration_0 -> aws:api_method:rest_api_0/rest_api_0_integration_0_method:
aws:api_integration:rest_api_0/rest_api_0_integration_0 -> aws:api_resource:rest_api_0/api_resource-1:
aws:api_integration:rest_api_0/rest_api_0_integration_0 -> rest_api/rest_api_0:
aws:api_resource:rest_api_0/api_resource-2:
aws:api_integration:rest_api_0/rest_api_0_integration_1:

aws:api_resource:rest_api_0/api_resource-2 -> rest_api/rest_api_0:
aws:api_integration:rest_api_0/rest_api_0_integration_1 -> aws:api_method:rest_api_0/rest_api_0_integration_1_method:
aws:api_integration:rest_api_0/rest_api_0_integration_1 -> aws:api_resource:rest_api_0/api_resource-2:
aws:api_integration:rest_api_0/rest_api_0_integration_1 -> rest_api/rest_api_0:
aws:api_integration:rest_api_0/rest_api_0_integration_2:

aws:api_integration:rest_api_0/rest_api_0_integration_2 -> aws:api_method:rest_api_0/rest_api_0_integration_2_method:
aws:api_integration:rest_api_0/rest_api_0_integration_2 -> aws:api_resource:rest_api_0/api_resource-2:
aws:api_integration:rest_api_0/rest_api_0_integration_2 -> rest_api/rest_api_0:
aws:api_deployment:rest_api_0/api_deployment-0:

aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_integration:rest_api_0/rest_api_0_integration_0:
aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_integration:rest_api_0/rest_api_0_integration_1:
aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_integration:rest_api_0/rest_api_0_integration_2:
aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_method:rest_api_0/rest_api_0_integration_0_method:
aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_method:rest_api_0/rest_api_0_integration_1_method:
aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_method:rest_api_0/rest_api_0_integration_2_method:
aws:api_deployment:rest_api_0/api_deployment-0 -> rest_api/rest_api_0:
aws:api_method:rest_api_0/rest_api_0_integration_2_method:

aws:api_method:rest_api_0/rest_api_0_integration_2_method -> aws:api_resource:rest_api_0/api_resource-2:
aws:api_method:rest_api_0/rest_api_0_integration_2_method -> rest_api/rest_api_0:
aws:api_method:rest_api_0/rest_api_0_integration_1_method:

aws:api_method:rest_api_0/rest_api_0_integration_1_method -> aws:api_resource:rest_api_0/api_resource-2:
aws:api_method:rest_api_0/rest_api_0_integration_1_method -> rest_api/rest_api_0:
aws:api_stage:rest_api_0/api_stage-0:

aws:api_stage:rest_api_0/api_stage-0 -> aws:api_deployment:rest_api_0/api_deployment-0:
aws:api_stage:rest_api_0/api_stage-0 -> rest_api/rest_api_0:
aws:api_integration:rest_api_0/rest_api_0_integration_2:

aws:api_integration:rest_api_0/rest_api_0_integration_2 -> aws:api_method:rest_api_0/rest_api_0_integration_2_method:
aws:api_integration:rest_api_0/rest_api_0_integration_2 -> aws:api_resource:rest_api_0/api_resource-2:
aws:api_integration:rest_api_0/rest_api_0_integration_2 -> rest_api/rest_api_0:
aws:api_integration:rest_api_0/rest_api_0_integration_1:

aws:api_integration:rest_api_0/rest_api_0_integration_1 -> aws:api_method:rest_api_0/rest_api_0_integration_1_method:
aws:api_integration:rest_api_0/rest_api_0_integration_1 -> aws:api_resource:rest_api_0/api_resource-2:
aws:api_integration:rest_api_0/rest_api_0_integration_1 -> rest_api/rest_api_0:
3 changes: 2 additions & 1 deletion pkg/engine2/testdata/static_site.expect.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
resources:
aws:cloudfront_distribution:cloudfront_distribution_1:
CloudfrontDefaultCertificate: true
ViewerCertificate:
CloudfrontDefaultCertificate: true
DefaultCacheBehavior:
AllowedMethods:
- DELETE
Expand Down
16 changes: 12 additions & 4 deletions pkg/infra/iac3/templates/aws/cloudfront_distribution/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,29 @@ import * as pulumi from '@pulumi/pulumi'
interface Args {
Name: string
Origins: aws.types.input.cloudfront.DistributionOrigin[]
CloudfrontDefaultCertificate: boolean
ViewerCertificate: aws.types.input.cloudfront.DistributionViewerCertificate
Enabled: boolean
DefaultCacheBehavior: aws.types.input.cloudfront.DistributionDefaultCacheBehavior
CacheBehaviors: aws.types.input.cloudfront.DistributionCacheBehavior[]
Restrictions: aws.types.input.cloudfront.DistributionRestrictions
DefaultRootObject: string
CNAMEs: string[]
CustomErrorResponses: aws.types.input.cloudfront.DistributionCustomErrorResponse[]
}

// noinspection JSUnusedLocalSymbols
function create(args: Args): aws.cloudfront.Distribution {
return new aws.cloudfront.Distribution(args.Name, {
origins: args.Origins,
enabled: args.Enabled,
viewerCertificate: {
cloudfrontDefaultCertificate: args.CloudfrontDefaultCertificate,
},
viewerCertificate: args.ViewerCertificate,
orderedCacheBehaviors: args.CacheBehaviors,
//TMPL {{- if .CNAMEs }}
aliases: args.CNAMEs,
//TMPL {{- end }}
//TMPL {{- if .CustomErrorResponses }}
customErrorResponses: args.CustomErrorResponses,
//TMPL {{- end }}
//TMPL {{- if (index .DefaultCacheBehavior "targetOriginId") }}
defaultCacheBehavior: args.DefaultCacheBehavior,
//TMPL {{- else }}
Expand Down
14 changes: 14 additions & 0 deletions pkg/knowledge_base2/dynamic_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ func (ctx DynamicValueContext) TemplateFunctions() template.FuncMap {
"add": add,
"sub": sub,
"last": last,
"makeSlice": func() []any {
return []any{}
},
"appendSlice": func(slice []any, value any) []any {
return append(slice, value)
},
"sliceContains": func(slice []any, value any) bool {
for _, v := range slice {
if v == value {
return true
}
}
return false
},
}
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/knowledge_base2/edge_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ type (
Source construct.ResourceId `yaml:"source"`
Target construct.ResourceId `yaml:"target"`

// AlwaysProcess signals that the edge should always be processed even if the source and target exist in the input graph
// currently we dont check edges for operational rules if they previously existed and this flag is set to false
AlwaysProcess bool `yaml:"always_process"`

// DirectEdgeOnly signals that the edge cannot be used within constructing other paths
// and can only be used as a direct edge
DirectEdgeOnly bool `yaml:"direct_edge_only"`
Expand Down
2 changes: 2 additions & 0 deletions pkg/knowledge_base2/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,13 @@ func firstFunctional(
func allDeps(
ids set.Set[construct.ResourceId],
) graph_addons.WalkGraphFunc[construct.ResourceId] {
resourceSet := set.Set[construct.ResourceId]{}
return func(path graph_addons.Path[construct.ResourceId], nerr error) error {
id := path[len(path)-1]
if ids != nil {
ids.Add(id)
}
resourceSet.Add(id)
return nil
}
}
Expand Down
17 changes: 17 additions & 0 deletions pkg/knowledge_base2/properties/int_property.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package properties

import (
"fmt"
"math"

construct "github.com/klothoplatform/klotho/pkg/construct2"
knowledgebase "github.com/klothoplatform/klotho/pkg/knowledge_base2"
Expand Down Expand Up @@ -57,6 +58,7 @@ func (i *IntProperty) GetDefaultValue(ctx knowledgebase.DynamicContext, data kno
}

func (i *IntProperty) Parse(value any, ctx knowledgebase.DynamicContext, data knowledgebase.DynamicValueData) (any, error) {

if val, ok := value.(string); ok {
var result int
err := ctx.ExecuteDecode(val, data, &result)
Expand All @@ -65,6 +67,21 @@ func (i *IntProperty) Parse(value any, ctx knowledgebase.DynamicContext, data kn
if val, ok := value.(int); ok {
return val, nil
}
EPSILON := 0.0000001
if val, ok := value.(float32); ok {
ival := int(val)
if math.Abs(float64(val)-float64(ival)) > EPSILON {
return 0, fmt.Errorf("cannot convert non-integral float to int: %f", val)
}
return int(val), nil

} else if val, ok := value.(float64); ok {
ival := int(val)
if math.Abs(val-float64(ival)) > EPSILON {
return 0, fmt.Errorf("cannot convert non-integral float to int: %f", val)
}
return int(val), nil
}
val, err := ParsePropertyRef(value, ctx, data)
if err == nil {
return val, nil
Expand Down
40 changes: 40 additions & 0 deletions pkg/knowledge_base2/properties/int_property_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,46 @@ func Test_IntProperty_Parse(t *testing.T) {
value: "{{ 1 }}",
expected: 1,
},
{
name: "valid float32 property",
property: &IntProperty{
PropertyDetails: knowledgebase2.PropertyDetails{
Path: "test",
},
},
value: float32(1.0),
expected: 1,
},
{
name: "invalid float32 property",
property: &IntProperty{
PropertyDetails: knowledgebase2.PropertyDetails{
Path: "test",
},
},
value: float32(1.1),
wantErr: true,
},
{
name: "valid float64 property",
property: &IntProperty{
PropertyDetails: knowledgebase2.PropertyDetails{
Path: "test",
},
},
value: float64(1.0),
expected: 1,
},
{
name: "invalid float64 property",
property: &IntProperty{
PropertyDetails: knowledgebase2.PropertyDetails{
Path: "test",
},
},
value: float64(1.1),
wantErr: true,
},
{
name: "non int property",
property: &IntProperty{
Expand Down
10 changes: 10 additions & 0 deletions pkg/knowledge_base2/properties/map_property.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ func (m *MapProperty) Parse(value any, ctx knowledgebase.DynamicContext, data kn
continue
}
result[key] = val
} else {
val, err := prop.GetDefaultValue(ctx, data)
if err != nil {
errs = errors.Join(errs, fmt.Errorf("unable to get default value for sub property %s: %w", key, err))
continue
}
if val == nil {
continue
}
result[key] = val
}
}
}
Expand Down
37 changes: 37 additions & 0 deletions pkg/knowledge_base2/properties/map_property_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,43 @@ func Test_MapProperty_Parse(t *testing.T) {
"value": "Name",
},
},
{
name: "parses sub properties",
property: &MapProperty{
Properties: knowledgebase2.Properties{
"key": &StringProperty{},
"value": &StringProperty{},
},
},
value: map[string]interface{}{
"key": "test",
"value": "test",
},
want: map[string]interface{}{
"key": "test",
"value": "test",
},
},
{
name: "parses sub properties with default values if they dont exist",
property: &MapProperty{
Properties: knowledgebase2.Properties{
"key": &StringProperty{},
"value": &StringProperty{
SharedPropertyFields: SharedPropertyFields{
DefaultValue: "test",
},
},
},
},
value: map[string]interface{}{
"key": "test",
},
want: map[string]interface{}{
"key": "test",
"value": "test",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions pkg/templates/aws/edges/api_deployment-rest_api.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
source: aws:api_deployment
target: aws:rest_api
always_process: true
operational_rules:
- if: '{{ hasDownstream "aws:api_integration" .Target }}'
steps:
Expand Down
Loading

0 comments on commit 84216dc

Please sign in to comment.