diff --git a/changelog/v1.15.0-beta15/scaled-snapshot-builder.yaml b/changelog/v1.15.0-beta15/scaled-snapshot-builder.yaml new file mode 100644 index 00000000000..c05ef8d44a2 --- /dev/null +++ b/changelog/v1.15.0-beta15/scaled-snapshot-builder.yaml @@ -0,0 +1,8 @@ +changelog: + - type: NON_USER_FACING + issueLink: https://github.com/solo-io/solo-projects/issues/5116 + resolvesIssue: true + description: >- + Generate scaled snapshots for performance tests via Builder paradigm for improved usability and extensibility + skipCI-kube-tests:true + skipCI-docs-build:true \ No newline at end of file diff --git a/projects/gloo/pkg/translator/performance_linux_test.go b/projects/gloo/pkg/translator/performance_linux_test.go index ce9875129be..8fa553f0afc 100644 --- a/projects/gloo/pkg/translator/performance_linux_test.go +++ b/projects/gloo/pkg/translator/performance_linux_test.go @@ -88,9 +88,10 @@ var _ = Describe("Translation - Benchmarking Tests", Serial, Label(labels.Perfor // We measure the duration of the translation of the snapshot, benchmarking according to the benchmarkConfig // Labels are used to add context to the entry description DescribeTable("Benchmark table", - func(apiSnap *v1snap.ApiSnapshot, config benchmarkConfig, labels ...string) { + func(snapBuilder *gloohelpers.ScaledSnapshotBuilder, config benchmarkConfig, labels ...string) { var ( - proxy *v1.Proxy + apiSnap *v1snap.ApiSnapshot + proxy *v1.Proxy snap cache.Snapshot errs reporter.ResourceReports @@ -99,6 +100,8 @@ var _ = Describe("Translation - Benchmarking Tests", Serial, Label(labels.Perfor tooFastWarningCount int ) + apiSnap = snapBuilder.Build() + params := plugins.Params{ Ctx: context.Background(), Snapshot: apiSnap, @@ -150,26 +153,15 @@ var _ = Describe("Translation - Benchmarking Tests", Serial, Label(labels.Perfor }, generateDesc, // generate descriptions for table entries with nil descriptions Entry("basic", basicSnap, basicConfig), - Entry(nil, gloohelpers.ScaledSnapshot(gloohelpers.ScaleConfig{ - Upstreams: 10, - Endpoints: 1, - }), basicConfig, "upstream scale"), - Entry(nil, gloohelpers.ScaledSnapshot(gloohelpers.ScaleConfig{ - Upstreams: 1000, - Endpoints: 1, - }), oneKUpstreamsConfig, "upstream scale"), - Entry(nil, gloohelpers.ScaledSnapshot(gloohelpers.ScaleConfig{ - Upstreams: 1, - Endpoints: 10, - }), basicConfig, "endpoint scale"), - Entry(nil, gloohelpers.ScaledSnapshot(gloohelpers.ScaleConfig{ - Upstreams: 1, - Endpoints: 1000, - }), basicConfig, "endpoint scale"), - Entry(nil, gloohelpers.ScaledSnapshot(gloohelpers.ScaleConfig{ - Upstreams: 10, - Endpoints: 10, - }), basicConfig, "endpoint scale", "upstream scale"), + Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(10).WithEndpointCount(1), basicConfig, "upstream scale"), + Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(1000).WithEndpointCount(1), oneKUpstreamsConfig, "upstream scale"), + Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(1).WithEndpointCount(10), basicConfig, "endpoint scale"), + Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(1).WithEndpointCount(1000), basicConfig, "endpoint scale"), + Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(10).WithEndpointCount(10), basicConfig, "endpoint scale", "upstream scale"), + Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(10).WithEndpointCount(1). + WithUpstreamBuilder(consistentSniUsBuilder), basicConfig, "consistent SNI", "upstream scale"), + Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(10).WithEndpointCount(1). + WithUpstreamBuilder(uniqueSniUsBuilder), basicConfig, "unique SNI", "upstream scale"), ) }) @@ -218,3 +210,9 @@ var oneKUpstreamsConfig = benchmarkConfig{ matchers.HavePercentileLessThan(90, 2*time.Second), }, } + +/* Upstream SNI Test */ +var ( + consistentSniUsBuilder = gloohelpers.NewUpstreamBuilder().WithConsistentSni() + uniqueSniUsBuilder = gloohelpers.NewUpstreamBuilder().WithUniqueSni() +) diff --git a/test/helpers/endpoints.go b/test/helpers/endpoints.go new file mode 100644 index 00000000000..930ab9400fd --- /dev/null +++ b/test/helpers/endpoints.go @@ -0,0 +1,15 @@ +package helpers + +import v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + +// EndpointBuilder contains options for building Endpoints to be included in scaled Snapshots +// there are no options currently configurable for the endpointBuilder +type EndpointBuilder struct{} + +func NewEndpointBuilder() *EndpointBuilder { + return &EndpointBuilder{} +} + +func (b *EndpointBuilder) Build(i int) *v1.Endpoint { + return Endpoint(i) +} diff --git a/test/helpers/resources.go b/test/helpers/scaled_snapshots.go similarity index 80% rename from test/helpers/resources.go rename to test/helpers/scaled_snapshots.go index b061983e878..2800266d9f1 100644 --- a/test/helpers/resources.go +++ b/test/helpers/scaled_snapshots.go @@ -3,24 +3,77 @@ package helpers import ( "fmt" + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/gloosnapshot" + "github.com/solo-io/gloo/projects/gloo/pkg/defaults" "github.com/golang/protobuf/ptypes/wrappers" v3 "github.com/solo-io/gloo/projects/gloo/pkg/api/external/envoy/config/core/v3" v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/core/matchers" - "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/gloosnapshot" v1static "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/options/static" "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/ssl" "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" ) -// ScaleConfig enumerates the number of each type of resource that should be included in a snapshot as generated by -// ScaledSnapshot +// ScaledSnapshotBuilder enumerates the number of each type of resource that should be included in a snapshot and +// contains a builder for each sub-resource type which is responsible for building instances of that resource // Additional fields should be added as needed -type ScaleConfig struct { - Endpoints int - Upstreams int +type ScaledSnapshotBuilder struct { + epCount int + usCount int + + epBuilder *EndpointBuilder + usBuilder *UpstreamBuilder +} + +func NewScaledSnapshotBuilder() *ScaledSnapshotBuilder { + return &ScaledSnapshotBuilder{ + epBuilder: NewEndpointBuilder(), + usBuilder: NewUpstreamBuilder(), + } +} + +func (b *ScaledSnapshotBuilder) WithUpstreamCount(n int) *ScaledSnapshotBuilder { + b.usCount = n + return b +} + +func (b *ScaledSnapshotBuilder) WithUpstreamBuilder(ub *UpstreamBuilder) *ScaledSnapshotBuilder { + b.usBuilder = ub + return b +} + +func (b *ScaledSnapshotBuilder) WithEndpointCount(n int) *ScaledSnapshotBuilder { + b.epCount = n + return b +} + +func (b *ScaledSnapshotBuilder) WithEndpointBuilder(eb *EndpointBuilder) *ScaledSnapshotBuilder { + b.epBuilder = eb + return b +} + +// Build generates a snapshot populated with the specified number of each resource for the builder, using the +// sub-resource builders to build each sub-resource +func (b *ScaledSnapshotBuilder) Build() *gloosnapshot.ApiSnapshot { + endpointList := make(v1.EndpointList, b.epCount) + for i := 0; i < b.epCount; i++ { + endpointList[i] = b.epBuilder.Build(i) + } + + upstreamList := make(v1.UpstreamList, b.usCount) + for i := 0; i < b.usCount; i++ { + upstreamList[i] = b.usBuilder.Build(i) + } + + return &gloosnapshot.ApiSnapshot{ + // The proxy should contain a route for each upstream + Proxies: []*v1.Proxy{Proxy(b.usCount)}, + + Endpoints: endpointList, + Upstreams: upstreamList, + } } func upMeta(i int) *core.Metadata { @@ -236,25 +289,3 @@ func Proxy(numRoutes int) *v1.Proxy { }, } } - -// ScaledSnapshot generates a snapshot populated with particular numbers of each resource types as determined by the -// passed config -func ScaledSnapshot(config ScaleConfig) *gloosnapshot.ApiSnapshot { - endpointList := make(v1.EndpointList, config.Endpoints) - for i := 0; i < config.Endpoints; i++ { - endpointList[i] = Endpoint(i + 1) // names are 1-indexed - } - - upstreamList := make(v1.UpstreamList, config.Upstreams) - for i := 0; i < config.Upstreams; i++ { - upstreamList[i] = Upstream(i + 1) // names are 1-indexed - } - - return &gloosnapshot.ApiSnapshot{ - // The proxy should contain a route for each upstream - Proxies: []*v1.Proxy{Proxy(config.Upstreams)}, - - Endpoints: endpointList, - Upstreams: upstreamList, - } -} diff --git a/test/helpers/scaled_snapshots_test.go b/test/helpers/scaled_snapshots_test.go new file mode 100644 index 00000000000..4f87f2b06a6 --- /dev/null +++ b/test/helpers/scaled_snapshots_test.go @@ -0,0 +1,56 @@ +package helpers_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/solo-io/gloo/test/helpers" +) + +var _ = Describe("ScaledSnapshotBuilder", func() { + When("with endpoints", func() { + It("generates a snapshot with the expected number of endpoints", func() { + snap := helpers.NewScaledSnapshotBuilder().WithEndpointCount(10).Build() + Expect(snap.Endpoints).To(HaveLen(10)) + Expect(snap.Upstreams).To(HaveLen(0)) + }) + }) + + When("with upstreams", func() { + It("generates a snapshot with the expected number of upstreams", func() { + snap := helpers.NewScaledSnapshotBuilder().WithUpstreamCount(10).Build() + Expect(snap.Endpoints).To(HaveLen(0)) + Expect(snap.Upstreams).To(HaveLen(10)) + }) + }) + + When("with upstream builder", func() { + When("with consistent SNI", func() { + It("generates a snapshot with upstreams that all have the same SNI", func() { + snap := helpers.NewScaledSnapshotBuilder().WithUpstreamCount(10). + WithUpstreamBuilder(helpers.NewUpstreamBuilder().WithConsistentSni()).Build() + Expect(snap.Upstreams).To(HaveLen(10)) + Expect(snap.Upstreams[0].SslConfig).NotTo(BeNil()) + firstSNI := snap.Upstreams[0].SslConfig.Sni + for i := 1; i < len(snap.Upstreams); i++ { + Expect(snap.Upstreams[i].SslConfig).NotTo(BeNil()) + Expect(snap.Upstreams[i].SslConfig.Sni).To(Equal(firstSNI)) + } + }) + }) + + When("with unique SNI", func() { + It("generates a snapshot with upstreams that all have unique SNI", func() { + snap := helpers.NewScaledSnapshotBuilder().WithUpstreamCount(10). + WithUpstreamBuilder(helpers.NewUpstreamBuilder().WithUniqueSni()).Build() + Expect(snap.Upstreams).To(HaveLen(10)) + foundSNI := map[string]bool{} + for i := 0; i < len(snap.Upstreams); i++ { + Expect(snap.Upstreams[i].SslConfig).NotTo(BeNil()) + _, ok := foundSNI[snap.Upstreams[i].SslConfig.Sni] + Expect(ok).To(BeFalse()) + foundSNI[snap.Upstreams[i].SslConfig.Sni] = true + } + }) + }) + }) +}) diff --git a/test/helpers/upstreams.go b/test/helpers/upstreams.go new file mode 100644 index 00000000000..6548aa38c75 --- /dev/null +++ b/test/helpers/upstreams.go @@ -0,0 +1,52 @@ +package helpers + +import ( + "fmt" + + v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/ssl" +) + +// UpstreamBuilder contains options for building Upstreams to be included in scaled Snapshots +type UpstreamBuilder struct { + sniPattern sniPattern +} + +type sniPattern int + +const ( + noSni sniPattern = iota + uniqueSni + consistentSni +) + +func NewUpstreamBuilder() *UpstreamBuilder { + return &UpstreamBuilder{} +} + +func (b *UpstreamBuilder) WithUniqueSni() *UpstreamBuilder { + b.sniPattern = uniqueSni + return b +} + +func (b *UpstreamBuilder) WithConsistentSni() *UpstreamBuilder { + b.sniPattern = consistentSni + return b +} + +func (b *UpstreamBuilder) Build(i int) *v1.Upstream { + up := Upstream(i) + + switch b.sniPattern { + case uniqueSni: + up.SslConfig = &ssl.UpstreamSslConfig{ + Sni: fmt.Sprintf("unique-domain-%d", i), + } + case consistentSni: + up.SslConfig = &ssl.UpstreamSslConfig{ + Sni: "consistent-domain", + } + } + + return up +} diff --git a/test/helpers/upstreams_test.go b/test/helpers/upstreams_test.go new file mode 100644 index 00000000000..5d27c2869f7 --- /dev/null +++ b/test/helpers/upstreams_test.go @@ -0,0 +1,39 @@ +package helpers_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/solo-io/gloo/test/helpers" +) + +var _ = Describe("UpstreamBuilder", func() { + When("using the base builder", func() { + It("generates an upstream without SslConfig", func() { + up := helpers.NewUpstreamBuilder().Build(1) + Expect(up.SslConfig).To(BeNil()) + }) + }) + + When("with consistent SNI", func() { + It("generates an upstream with the same SNI for any i", func() { + up1 := helpers.NewUpstreamBuilder().WithConsistentSni().Build(1) + Expect(up1.SslConfig).NotTo(BeNil()) + up2 := helpers.NewUpstreamBuilder().WithConsistentSni().Build(2) + Expect(up2.SslConfig).NotTo(BeNil()) + + Expect(up1.SslConfig.Sni).To(Equal(up2.SslConfig.Sni)) + }) + }) + + When("with unique SNI", func() { + It("generates an upstream with unique SNI for a given i", func() { + up1 := helpers.NewUpstreamBuilder().WithUniqueSni().Build(1) + Expect(up1.SslConfig).NotTo(BeNil()) + up2 := helpers.NewUpstreamBuilder().WithUniqueSni().Build(2) + Expect(up2.SslConfig).NotTo(BeNil()) + + Expect(up1.SslConfig.Sni).NotTo(Equal(up2.SslConfig.Sni)) + }) + }) + +})