Skip to content

Commit

Permalink
Automate scale test
Browse files Browse the repository at this point in the history
  • Loading branch information
ciarams87 committed Apr 3, 2024
1 parent 0765072 commit 134cea8
Show file tree
Hide file tree
Showing 22 changed files with 758 additions and 361 deletions.
19 changes: 15 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go 1.22.0
replace github.com/chzyer/logex v1.1.10 => github.com/chzyer/logex v1.2.0

require (
cloud.google.com/go/monitoring v1.18.0
github.com/go-kit/log v0.2.1
github.com/go-logr/logr v1.4.1
github.com/google/go-cmp v0.6.0
Expand All @@ -23,6 +24,8 @@ require (
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
go.uber.org/zap v1.27.0
google.golang.org/api v0.162.0
google.golang.org/protobuf v1.33.0
k8s.io/api v0.29.3
k8s.io/apiextensions-apiserver v0.29.3
k8s.io/apimachinery v0.29.3
Expand All @@ -34,6 +37,8 @@ require (
)

require (
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
Expand All @@ -57,7 +62,10 @@ require (
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
Expand All @@ -79,12 +87,15 @@ require (
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect
github.com/stretchr/testify v1.8.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.22.0 // indirect
Expand All @@ -93,14 +104,14 @@ require (
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.19.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect
google.golang.org/grpc v1.61.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
106 changes: 98 additions & 8 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ stop-longevity-test: ## Stops the longevity test and collects results

.PHONY: .vm-nfr-test
.vm-nfr-test: ## Runs the NFR tests on the GCP VM (called by `nfr-test`)
go test -v ./suite -ginkgo.label-filter "nfr" $(GINKGO_FLAGS) -ginkgo.v -args --gateway-api-version=$(GW_API_VERSION) \
go test -v -timeout 1h ./suite -ginkgo.label-filter "nfr" $(GINKGO_FLAGS) -ginkgo.v -args --gateway-api-version=$(GW_API_VERSION) \
--gateway-api-prev-version=$(GW_API_PREV_VERSION) --image-tag=$(TAG) --version-under-test=$(NGF_VERSION) \
--plus-enabled=$(PLUS_ENABLED) --ngf-image-repo=$(PREFIX) --nginx-image-repo=$(NGINX_PREFIX) --nginx-plus-image-repo=$(NGINX_PLUS_PREFIX) \
--pull-policy=$(PULL_POLICY) --k8s-version=$(K8S_VERSION) --service-type=$(GW_SERVICE_TYPE) \
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
//go:build scale
// +build scale

package scale
package framework

import (
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
)

Expand Down Expand Up @@ -63,8 +61,7 @@ data:
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzZtTnJSdUZ2WXZoSE4KbXI3c1FvNUtKSUVDN3N6TFVrNExFeklSNS9yMEVaUjQ2RnRTaGJQd0ZuaXAwMFBxekhpVkhKYy92TjdkQTVLeApQS1VmdFJuQ1J6YldVaTZBZzJpRU93bXF6WUhGbVNpZkFlVjk0RlAxOGtSbjl1ckV3OEpiRXJIUncrVW51L25tCmFMRHF1eGpFTVBweGhuRklCSnYwK1R3djNEVGx6TjNwUlV6dnpidGZvZCtEVTZBSmR6N3Rid1dTNmR6MHc1Z2kKbW9RelZnbFpnVDBJek9FZkV3NVpWMnRMZllHZWRlRVJ1VjhtR041c09va3R2aGxsMU1udHRaMkZNVHgySmVjUQo3K0xBRm9YVnBTS2NjbUFVZ1JBM0xOOHdVZXBVTHZZdFhiUm1QTFc4SjFINmhFeHJHTHBiTERZNmpzbGxBNlZpCk0xMjVjU0hsQWdNQkFBRUNnZ0VBQnpaRE50bmVTdWxGdk9HZlFYaHRFWGFKdWZoSzJBenRVVVpEcUNlRUxvekQKWlV6dHdxbkNRNlJLczUyandWNTN4cU9kUU94bTNMbjNvSHdNa2NZcEliWW82MjJ2dUczYnkwaVEzaFlsVHVMVgpqQmZCcS9UUXFlL2NMdngvSkczQWhFNmJxdFRjZFlXeGFmTmY2eUtpR1dzZk11WVVXTWs4MGVJVUxuRmZaZ1pOCklYNTlSOHlqdE9CVm9Sa3hjYTVoMW1ZTDFsSlJNM3ZqVHNHTHFybmpOTjNBdWZ3ZGRpK1VDbGZVL2l0K1EvZkUKV216aFFoTlRpNVFkRWJLVStOTnYvNnYvb2JvandNb25HVVBCdEFTUE05cmxFemIralQ1WHdWQjgvLzRGY3VoSwoyVzNpcjhtNHVlQ1JHSVlrbGxlLzhuQmZ0eVhiVkNocVRyZFBlaGlPM1FLQmdRRGlrR3JTOTc3cjg3Y1JPOCtQClpoeXltNXo4NVIzTHVVbFNTazJiOTI1QlhvakpZL2RRZDVTdFVsSWE4OUZKZnNWc1JRcEhHaTFCYzBMaTY1YjIKazR0cE5xcVFoUmZ1UVh0UG9GYXRuQzlPRnJVTXJXbDVJN0ZFejZnNkNQMVBXMEg5d2hPemFKZUdpZVpNYjlYTQoybDdSSFZOcC9jTDlYbmhNMnN0Q1lua2Iwd0tCZ1FEUzF4K0crakEyUVNtRVFWNXA1RnRONGcyamsyZEFjMEhNClRIQ2tTazFDRjhkR0Z2UWtsWm5ZbUt0dXFYeXNtekJGcnZKdmt2eUhqbUNYYTducXlpajBEdDZtODViN3BGcVAKQWxtajdtbXI3Z1pUeG1ZMXBhRWFLMXY4SDNINGtRNVl3MWdrTWRybVJHcVAvaTBGaDVpaGtSZS9DOUtGTFVkSQpDcnJjTzhkUVp3S0JnSHA1MzRXVWNCMVZibzFlYStIMUxXWlFRUmxsTWlwRFM2TzBqeWZWSmtFb1BZSEJESnp2ClIrdzZLREJ4eFoyWmJsZ05LblV0YlhHSVFZd3lGelhNcFB5SGxNVHpiZkJhYmJLcDFyR2JVT2RCMXpXM09PRkgKcmppb21TUm1YNmxhaDk0SjRHU0lFZ0drNGw1SHhxZ3JGRDZ2UDd4NGRjUktJWFpLZ0w2dVJSSUpBb0dCQU1CVApaL2p5WStRNTBLdEtEZHUrYU9ORW4zaGxUN3hrNXRKN3NBek5rbWdGMU10RXlQUk9Xd1pQVGFJbWpRbk9qbHdpCldCZ2JGcXg0M2ZlQ1Z4ZXJ6V3ZEM0txaWJVbWpCTkNMTGtYeGh3ZEVteFQwVit2NzZGYzgwaTNNYVdSNnZZR08KditwVVovL0F6UXdJcWZ6dlVmV2ZxdStrMHlhVXhQOGNlcFBIRyt0bEFvR0FmQUtVVWhqeFU0Ym5vVzVwVUhKegpwWWZXZXZ5TW54NWZyT2VsSmRmNzlvNGMvMHhVSjh1eFBFWDFkRmNrZW96dHNpaVFTNkN6MENRY09XVWxtSkRwCnVrdERvVzM3VmNSQU1BVjY3NlgxQVZlM0UwNm5aL2g2Tkd4Z28rT042Q3pwL0lkMkJPUm9IMFAxa2RjY1NLT3kKMUtFZlNnb1B0c1N1eEpBZXdUZmxDMXc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
`

var appTmplTxt = `apiVersion: v1
apiVersion: apps/v1
var appTmplTxt = `apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ . }}
Expand Down Expand Up @@ -118,11 +115,11 @@ type Route struct {
BackendName string
}

func getPrereqDirName(manifestDir string) string {
return filepath.Join(manifestDir, "prereqs")
func GetPrereqDirName(manifestDir string) string {
return filepath.Join(manifestDir, "manifests/prereqs")
}

func generateScaleListenerManifests(numListeners int, manifestDir string, tls bool) error {
func GenerateScaleListenerManifests(numListeners int, manifestDir string, tls bool) error {
listeners := make([]Listener, 0)
backends := make([]string, 0)
secrets := make([]string, 0)
Expand Down Expand Up @@ -158,11 +155,11 @@ func generateScaleListenerManifests(numListeners int, manifestDir string, tls bo
}
}

if err := generateSecrets(getPrereqDirName(manifestDir), secrets); err != nil {
if err := generateSecrets(GetPrereqDirName(manifestDir), secrets); err != nil {
return err
}

return generateBackendAppManifests(getPrereqDirName(manifestDir), backends)
return generateBackendAppManifests(GetPrereqDirName(manifestDir), backends)
}

func generateSecrets(secretsDir string, secrets []string) error {
Expand All @@ -180,7 +177,6 @@ func generateSecrets(secretsDir string, secrets []string) error {

path := filepath.Join(secretsDir, fmt.Sprintf("%s.yaml", secret))

fmt.Println("Writing", path)
if err := os.WriteFile(path, buf.Bytes(), 0o600); err != nil {
return err
}
Expand All @@ -189,7 +185,7 @@ func generateSecrets(secretsDir string, secrets []string) error {
return nil
}

func generateScaleHTTPRouteManifests(numRoutes int, manifestDir string) error {
func GenerateScaleHTTPRouteManifests(numRoutes int, manifestDir string) error {
l := Listener{
Name: "listener",
HostnamePrefix: "*",
Expand Down Expand Up @@ -218,10 +214,12 @@ func generateScaleHTTPRouteManifests(numRoutes int, manifestDir string) error {

}

return generateBackendAppManifests(getPrereqDirName(manifestDir), []string{backendName})
return generateBackendAppManifests(GetPrereqDirName(manifestDir), []string{backendName})
}

func generateManifests(outDir string, version int, listeners []Listener, routes []Route) error {
manifestDir := filepath.Join(outDir, "manifests")

var buf bytes.Buffer

if len(listeners) > 0 {
Expand All @@ -240,15 +238,13 @@ func generateManifests(outDir string, version int, listeners []Listener, routes
}
}

err := os.Mkdir(outDir, 0o750)
if err != nil && !os.IsExist(err) {
if err := os.MkdirAll(manifestDir, 0o750); err != nil {
return err
}

filename := fmt.Sprintf("manifest-%d.yaml", version)
path := filepath.Join(outDir, filename)
path := filepath.Join(manifestDir, filename)

fmt.Println("Writing", path)
return os.WriteFile(path, buf.Bytes(), 0o600)
}

Expand All @@ -267,11 +263,27 @@ func generateBackendAppManifests(outDir string, backends []string) error {

path := filepath.Join(outDir, fmt.Sprintf("%s.yaml", backend))

fmt.Println("Writing", path)
if err := os.WriteFile(path, buf.Bytes(), 0o600); err != nil {
return err
}
}

return nil
}

func GetYamlFileList(manifestDir string) ([]string, error) {
var yamlFiles []string
files, err := os.ReadDir(manifestDir)
if err != nil {
return yamlFiles, fmt.Errorf("error reading directory: %v", err)

Check failure on line 278 in tests/framework/generate_manifests.go

View workflow job for this annotation

GitHub Actions / Lint

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)
}

// Loop through each file
for _, file := range files {
// Check if it's a YAML file
if strings.HasSuffix(file.Name(), ".yaml") || strings.HasSuffix(file.Name(), ".yml") {
yamlFiles = append(yamlFiles, filepath.Join(manifestDir, file.Name()))
}
}
return yamlFiles, nil
}
145 changes: 145 additions & 0 deletions tests/framework/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package framework

import (
"context"
"encoding/csv"
"errors"
"fmt"
"strconv"
"strings"
"time"

monitoring "cloud.google.com/go/monitoring/apiv3/v2"
"cloud.google.com/go/monitoring/apiv3/v2/monitoringpb"

"google.golang.org/api/iterator"
"google.golang.org/protobuf/types/known/timestamppb"
)

const (
CpuMetricName = "kubernetes.io/container/cpu/core_usage_time"

Check warning on line 20 in tests/framework/metrics.go

View workflow job for this annotation

GitHub Actions / Lint

var-naming: const CpuMetricName should be CPUMetricName (revive)
MemoryMetricName = "kubernetes.io/container/memory/used_bytes"
PromReloads = "prometheus.googleapis.com/nginx_gateway_fabric_nginx_reloads_total/counter"
)

// NGFPrometheusMetrics are the relevant NGFPrometheusMetrics for scale tests
type NGFPrometheusMetrics struct {
ReloadCount string
ReloadErrsCount string
ReloadAvgTime string
ReloadsUnder500ms string
EventsCount string
EventsErrsCount string
EventsAvgTime string
EventsUnder500ms string
}

// WriteGKEMetricsData gathers the requested timeseries metrics data and writes it to the given CSV file.
func WriteGKEMetricsData(podName, metricType string, startTime, endTime int64, csvWriter *csv.Writer) error {
// Create a context and specify the desired project ID.
ctx := context.Background()
// projectID := os.Getenv("GKE_PROJECT")
// TODO: Remove hardcoded project ID
projectID := ""

// Create a MetricsClient for the Google Cloud Monitoring API.
metricsClient, err := monitoring.NewMetricClient(ctx)
if err != nil {
return fmt.Errorf("failed to create MetricsClient: %v", err)

Check failure on line 48 in tests/framework/metrics.go

View workflow job for this annotation

GitHub Actions / Lint

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)
}
defer metricsClient.Close()

// Create a filter string to retrieve the required metric for the specific pod.
metricValueName := strings.Split(metricType, "/")[3]
valueAggregate := fmt.Sprintf("value_aggregate: aggregate(value.%s)", metricValueName)
filter := fmt.Sprintf(`resource.type = "k8s_container" AND resource.label.pod_name = "%s" AND metric.type = "%s"`, podName, metricType)

Check failure on line 55 in tests/framework/metrics.go

View workflow job for this annotation

GitHub Actions / Lint

line is 136 characters (lll)
aggregation := &monitoringpb.Aggregation{
GroupByFields: []string{valueAggregate, "max(value_aggregate)"},
}

fmt.Println("Filter: ", filter)
// Prepare the request to retrieve time series data.
req := &monitoringpb.ListTimeSeriesRequest{
Name: "projects/" + projectID,
Filter: filter,
Aggregation: aggregation,
Interval: &monitoringpb.TimeInterval{
StartTime: &timestamppb.Timestamp{Seconds: startTime},
EndTime: &timestamppb.Timestamp{Seconds: endTime},
},
}

fmt.Println("start-time", startTime, "end-time", endTime)

dataFound, err := writeMetricsData(ctx, metricsClient, req, csvWriter)

Check failure on line 75 in tests/framework/metrics.go

View workflow job for this annotation

GitHub Actions / Lint

File is not `gofumpt`-ed (gofumpt)
if err != nil {
return err
}

if !dataFound {
// no data yet, wait and try again with new timestamp
time.Sleep(1 * time.Minute)
req.Interval.EndTime = &timestamppb.Timestamp{Seconds: time.Now().Unix()}
dataFound, err = writeMetricsData(ctx, metricsClient, req, csvWriter)
if err != nil {
return err
}
if !dataFound {
fmt.Println("Waited for 1 minute but still no data for time period, exiting...")
}
}
return nil
}

func writeMetricsData(ctx context.Context, metricsClient *monitoring.MetricClient, req *monitoringpb.ListTimeSeriesRequest, csvWriter *csv.Writer) (bool, error) {

Check failure on line 95 in tests/framework/metrics.go

View workflow job for this annotation

GitHub Actions / Lint

line is 162 characters (lll)
var dataFound bool
// Retrieve time series data.
it := metricsClient.ListTimeSeries(ctx, req)

for {
ts, err := it.Next()
if errors.Is(err, iterator.Done) {
fmt.Println("no more entries in iterator")
break
}
if err != nil {
return false, fmt.Errorf("failed to iterate time series data: %v", err)

Check failure on line 107 in tests/framework/metrics.go

View workflow job for this annotation

GitHub Actions / Lint

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)
}
fmt.Println("Points in time series: ", len(ts.Points))
for _, point := range ts.Points {
timestamp := point.Interval.EndTime.Seconds
fmt.Println()
data := fmt.Sprintf("%.2f", point.GetValue().GetDoubleValue())
if data != "0.00" {
dataFound = true
err = csvWriter.Write([]string{strconv.FormatInt(timestamp, 10), data})
fmt.Println("Timestamp: ", strconv.FormatInt(timestamp, 10), timestamp)
if err != nil {
return false, fmt.Errorf("failed to write data to CSV file: %v", err)
}
} else {
fmt.Println("No data yet, try again...")
}
}
}
return dataFound, nil
}

//func getPrometheusMetricsData() (error, NGFPrometheusMetrics) {
// // Create a context and specify the desired project ID.
// ctx := context.Background()
// // projectID := os.Getenv("GKE_PROJECT")
// // TODO: Remove hardcoded project ID
// projectID := "f5-gcs-7899-ptg-ingrss-ctlr"
//
// var ngfPromMetrics NGFPrometheusMetrics
//
// // Create a MetricsClient for the Google Cloud Monitoring API.
// metricsClient, err := monitoring.NewMetricClient(ctx)
// if err != nil {
// return fmt.Errorf("failed to create MetricsClient: %v", err), ngfPromMetrics
// }
// defer metricsClient.Close()
//
//}
Loading

0 comments on commit 134cea8

Please sign in to comment.