From bc517e01e9992dbae51e28b6bccc1991afbdc12c Mon Sep 17 00:00:00 2001 From: Mathew Merrick Date: Thu, 10 Oct 2024 21:55:35 +0000 Subject: [PATCH 1/6] pprof --- test/e2e/framework/kubernetes/pprof.go | 202 +++++++++++++++++++++ test/e2e/jobs/logrunning.go | 12 ++ test/e2e/retina_longrunning_test.go | 27 +++ test/e2e/scenarios/longrunning/scenario.go | 54 ++++++ test/e2e/settings_test.go | 83 +++++++++ 5 files changed, 378 insertions(+) create mode 100644 test/e2e/framework/kubernetes/pprof.go create mode 100644 test/e2e/jobs/logrunning.go create mode 100644 test/e2e/retina_longrunning_test.go create mode 100644 test/e2e/scenarios/longrunning/scenario.go create mode 100644 test/e2e/settings_test.go diff --git a/test/e2e/framework/kubernetes/pprof.go b/test/e2e/framework/kubernetes/pprof.go new file mode 100644 index 0000000000..b331f6a651 --- /dev/null +++ b/test/e2e/framework/kubernetes/pprof.go @@ -0,0 +1,202 @@ +package kubernetes + +import ( + "context" + "fmt" + "io" + "log" + "net/http" + "net/url" + "os" + "strconv" + "time" +) + +const ( + defaultTimeout = 30 * time.Second + defaultRetinaPort = 10093 + defaultSpanTime = 10 * time.Second +) + +var ( + + // key:profile name, value: pprof endpoint + simpleProfiles = map[string]string{ + "heap": "/heap", + "block": "/block", + "mutex": "/mutex", + } + + durationProfiles = map[string]string{ + "cpu": "/profile", + "trace": "/trace", + "goroutine": "/goroutine", + } +) + +type PullPProf struct { + LocalPort string + DurationSeconds string // full duration which includes as many intervals as possible + ScrapeIntervalSeconds string // will pull all profiles at this interval + + scraper *PprofScraper +} + +func (p *PullPProf) Run() error { + host := "localhost" + var err error + p.scraper, err = NewPprofScraper(host, defaultRetinaPort) + if err != nil { + return err + } + + duration, err := strconv.Atoi(p.DurationSeconds) + if err != nil { + return fmt.Errorf("error converting pprof duration to int: %w", err) + } + interval, err := strconv.Atoi(p.ScrapeIntervalSeconds) + if err != nil { + return fmt.Errorf("error converting pprof interval to int: %w", err) + } + + ticker := time.NewTicker(time.Duration(interval) * time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(duration)*time.Second) + defer cancel() + + defer func() { + if err != nil { + log.Printf("--pprof viewing commands:--\n") + log.Printf("heap: \tgo tool pprof -http=:8081 %s\n", "heap.out") + log.Printf("cpu: \tgo tool pprof -http=:8082 %s\n", "cpu.out") + log.Printf("block: \tgo tool pprof -http=:8083 %s\n", "block.out") + log.Printf("mutex: \tgo tool pprof -http=:8084 %s\n", "mutex.out") + log.Printf("trace: \tgo tool trace -http=:8085 %s\n", "trace.out") + } + }() + + scrape := func() error { + log.Printf("-- scraping pprof profiles --\n") + folder := "./pprof/" + time.Now().Format("2006.01.02-15:04:05") + "/" + err = os.MkdirAll(folder, os.ModePerm) + if err != nil { + return fmt.Errorf("error creating pprof folder: %w", err) + } + + for name, path := range simpleProfiles { + file := folder + name + ".out" + err = p.scraper.GetProfile(name, path, file) + if err != nil { + // don't return here because some data is better than no data, + // and other profiles might be functional + log.Printf("error getting %s profile: %v\n", name, err) + } + } + + for name, path := range durationProfiles { + file := folder + name + ".out" + err = p.scraper.GetProfileWithDuration(name, path, file, defaultSpanTime) + if err != nil { + // don't return here because some data is better than no data, + // and other profiles might be functional + log.Printf("error getting %s profile: %v\n", name, err) + } + } + + log.Printf("-- scraped profiles saved to to %s --\n", folder) + return err + } + + // pull initial scrape + err = scrape() + if err != nil { + return fmt.Errorf("error pulling pprof profiles: %w", err) + } + + // start scraping on intervals + for { + select { + case <-ctx.Done(): + if err != nil { + return fmt.Errorf("error pulling pprof profiles: %w", err) + } + return nil + case <-ticker.C: + err = scrape() + } + } +} + +func (p *PullPProf) Prevalidate() error { + return nil +} + +func (p *PullPProf) Stop() error { + return nil +} + +type PprofScraper struct { + baseURL *url.URL +} + +func NewPprofScraper(host string, port int) (*PprofScraper, error) { + scraper := &PprofScraper{} + var err error + baseURL := fmt.Sprintf("http://%s:%d/debug/pprof", host, port) + scraper.baseURL, err = url.Parse(baseURL) + if err != nil { + return nil, fmt.Errorf("error parsing URL: %w", err) + } + return scraper, nil +} + +func (p *PprofScraper) GetProfileWithDuration(name, path, outfile string, duration time.Duration) error { + seconds := int(duration.Seconds()) + log.Printf("getting %s profile for %d seconds...\n", name, seconds) + profileURL := p.formatURLWithSeconds(seconds) + profileURL.Path += path + return p.scrape(profileURL.String(), defaultTimeout+duration, outfile) +} + +func (p *PprofScraper) GetProfile(name, path, outfile string) error { + log.Printf("getting %s profile...\n", name) + return p.scrape(p.baseURL.String()+path, defaultTimeout, outfile) +} + +func (p *PprofScraper) formatURLWithSeconds(seconds int) url.URL { + // Add query parameters + queryURL := *p.baseURL + q := queryURL.Query() + q.Set("seconds", strconv.Itoa(seconds)) + queryURL.RawQuery = q.Encode() + return queryURL +} + +func (p *PprofScraper) scrape(scrapingURL string, timeout time.Duration, outfile string) error { + client := http.Client{ + Timeout: timeout, + } + + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, scrapingURL, http.NoBody) + if err != nil { + return fmt.Errorf("error creating request: %w", err) + } + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("error scraping: %w", err) + } + defer resp.Body.Close() + + file, err := os.Create(outfile) + if err != nil { + return fmt.Errorf("error creating file: %w", err) + } + defer file.Close() + + _, err = io.Copy(file, resp.Body) + if err != nil { + return fmt.Errorf("error copying scrape response to file: %w", err) + } + + return nil +} diff --git a/test/e2e/jobs/logrunning.go b/test/e2e/jobs/logrunning.go new file mode 100644 index 0000000000..1002cb4fc6 --- /dev/null +++ b/test/e2e/jobs/logrunning.go @@ -0,0 +1,12 @@ +package retina + +import ( + "github.com/microsoft/retina/test/e2e/framework/types" + "github.com/microsoft/retina/test/e2e/scenarios/longrunning" +) + +func CreateLongRunningTest(subID, clusterName, location, kubeConfigFilePath string, createInfra bool) *types.Job { + job := types.NewJob("Run Retina over a long period of time") + job.AddScenario(longrunning.PullPProf(kubeConfigFilePath)) + return job +} diff --git a/test/e2e/retina_longrunning_test.go b/test/e2e/retina_longrunning_test.go new file mode 100644 index 0000000000..3543d3c24b --- /dev/null +++ b/test/e2e/retina_longrunning_test.go @@ -0,0 +1,27 @@ +package retina + +import ( + "testing" + + "github.com/microsoft/retina/test/e2e/framework/types" + jobs "github.com/microsoft/retina/test/e2e/jobs" + "github.com/stretchr/testify/require" +) + +// TestE2ERetina tests all e2e scenarios for retina +func TestLongRunningRetina(t *testing.T) { + settings, err := LoadInfraSettings() + require.NoError(t, err) + + // CreateTestInfra + createTestInfra := types.NewRunner(t, jobs.CreateTestInfra(subID, clusterName, location, settings.KubeConfigFilePath, settings.CreateInfra)) + createTestInfra.Run() + t.Cleanup(func() { + if settings.DeleteInfra { + _ = jobs.DeleteTestInfra(subID, clusterName, location).Run() + } + }) + + longrunning := types.NewRunner(t, jobs.CreateLongRunningTest(subID, clusterName, location, settings.KubeConfigFilePath, settings.CreateInfra)) + longrunning.Run() +} diff --git a/test/e2e/scenarios/longrunning/scenario.go b/test/e2e/scenarios/longrunning/scenario.go new file mode 100644 index 0000000000..e685543864 --- /dev/null +++ b/test/e2e/scenarios/longrunning/scenario.go @@ -0,0 +1,54 @@ +package longrunning + +import ( + "github.com/microsoft/retina/test/e2e/framework/kubernetes" + "github.com/microsoft/retina/test/e2e/framework/types" +) + +func PullPProf(kubeConfigFilePath string) *types.Scenario { + Name := "Pull PProf" + Steps := []*types.StepWrapper{ + { + Step: &kubernetes.CreateKapingerDeployment{ + KapingerNamespace: "kube-system", + KapingerReplicas: "5", + KubeConfigFilePath: kubeConfigFilePath, + }, + }, + { + Step: &kubernetes.PortForward{ + Namespace: "kube-system", + LabelSelector: "k8s-app=retina", + LocalPort: "10093", + RemotePort: "10093", + Endpoint: "metrics", + OptionalLabelAffinity: "app=kapinger", + }, + Opts: &types.StepOptions{ + RunInBackgroundWithID: "retina-port-forward", + }, + }, + { + Step: &kubernetes.PullPProf{ + DurationSeconds: "28800", // 8 hours + ScrapeIntervalSeconds: "60", + }, + }, + { + Step: &types.Stop{ + BackgroundID: "retina-port-forward", + }, + }, + { + Step: &kubernetes.DeleteKubernetesResource{ + ResourceType: kubernetes.TypeString(kubernetes.Deployment), + ResourceName: "kapinger", + ResourceNamespace: "kube-system", + }, Opts: &types.StepOptions{ + SkipSavingParametersToJob: true, + }, + }, + } + + return types.NewScenario(Name, Steps...) +} diff --git a/test/e2e/settings_test.go b/test/e2e/settings_test.go new file mode 100644 index 0000000000..1ed4570b43 --- /dev/null +++ b/test/e2e/settings_test.go @@ -0,0 +1,83 @@ +package retina + +import ( + "crypto/rand" + "flag" + "fmt" + "log" + "math/big" + "os" + "os/user" + "path/filepath" + "strconv" + "time" + + "github.com/microsoft/retina/test/e2e/common" +) + +var ( + clusterName = os.Getenv("CLUSTER_NAME") + subID = os.Getenv("AZURE_SUBSCRIPTION_ID") + location = os.Getenv("AZURE_LOCATION") +) + +var ( + locations = []string{"eastus2", "centralus", "southcentralus", "uksouth", "centralindia", "westus2"} + createInfra = flag.Bool("create-infra", true, "create a Resource group, vNET and AKS cluster for testing") + deleteInfra = flag.Bool("delete-infra", true, "delete a Resource group, vNET and AKS cluster for testing") +) + +type TestInfraSettings struct { + CreateInfra bool + DeleteInfra bool + ChartPath string + ProfilePath string + KubeConfigFilePath string +} + +func LoadInfraSettings() (*TestInfraSettings, error) { + curuser, err := user.Current() + if err != nil { + return nil, err + } + + flag.Parse() + + if clusterName == "" { + clusterName = curuser.Username + common.NetObsRGtag + strconv.FormatInt(time.Now().Unix(), 10) + log.Printf("CLUSTER_NAME is not set, generating a random cluster name: %s", clusterName) + } + + if subID == "" { + return nil, fmt.Errorf("AZURE_SUBSCRIPTION_ID is not set") + } + + if location == "" { + var nBig *big.Int + nBig, err = rand.Int(rand.Reader, big.NewInt(int64(len(locations)))) + if err != nil { + return nil, fmt.Errorf("Failed to generate a secure random index: %v", err) + } + location = locations[nBig.Int64()] + } + + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + + // Get to root of the repo by going up two directories + rootDir := filepath.Dir(filepath.Dir(cwd)) + + chartPath := filepath.Join(rootDir, "deploy", "legacy", "manifests", "controller", "helm", "retina") + profilePath := filepath.Join(rootDir, "test", "profiles", "advanced", "values.yaml") + kubeConfigFilePath := filepath.Join(rootDir, "test", "e2e", "test.pem") + + return &TestInfraSettings{ + CreateInfra: *createInfra, + DeleteInfra: *deleteInfra, + ChartPath: chartPath, + ProfilePath: profilePath, + KubeConfigFilePath: kubeConfigFilePath, + }, nil +} From 3f52a6ab6dbffd887218af994cac2c8105668a54 Mon Sep 17 00:00:00 2001 From: Mathew Merrick Date: Thu, 10 Oct 2024 22:00:00 +0000 Subject: [PATCH 2/6] cleaner output --- .gitignore | 1 + test/e2e/framework/kubernetes/pprof.go | 5 +++-- test/e2e/retina_longrunning_test.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 026134d26a..9a64c44b2a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.dll *.so *.dylib +*__debug* # Avoid checking in keys *.pem diff --git a/test/e2e/framework/kubernetes/pprof.go b/test/e2e/framework/kubernetes/pprof.go index b331f6a651..56451488a8 100644 --- a/test/e2e/framework/kubernetes/pprof.go +++ b/test/e2e/framework/kubernetes/pprof.go @@ -75,7 +75,7 @@ func (p *PullPProf) Run() error { }() scrape := func() error { - log.Printf("-- scraping pprof profiles --\n") + log.Printf("-- starting scrape pprof profiles --\n") folder := "./pprof/" + time.Now().Format("2006.01.02-15:04:05") + "/" err = os.MkdirAll(folder, os.ModePerm) if err != nil { @@ -102,7 +102,8 @@ func (p *PullPProf) Run() error { } } - log.Printf("-- scraped profiles saved to to %s --\n", folder) + log.Printf("-- finished scraping profiles, saved to to %s --\n", folder) + log.Printf("waiting %s seconds for next scrape\n", p.ScrapeIntervalSeconds) return err } diff --git a/test/e2e/retina_longrunning_test.go b/test/e2e/retina_longrunning_test.go index 3543d3c24b..60483490c2 100644 --- a/test/e2e/retina_longrunning_test.go +++ b/test/e2e/retina_longrunning_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" ) -// TestE2ERetina tests all e2e scenarios for retina +// Scrape PProf over a long running datapath tests func TestLongRunningRetina(t *testing.T) { settings, err := LoadInfraSettings() require.NoError(t, err) From decf9cdfe7229db4bd3a61dbce61d158bee3705c Mon Sep 17 00:00:00 2001 From: Mathew Merrick Date: Fri, 11 Oct 2024 22:31:48 +0000 Subject: [PATCH 3/6] kapinger refinements --- ...monitoring.coreos.com_servicemonitors.yaml | 1216 +++++++++++++++++ hack/tools/kapinger/clients/http.go | 5 +- hack/tools/kapinger/servers/http.go | 2 +- hack/tools/kapinger/servers/util.go | 2 +- test/e2e/Makefile | 16 + .../framework/kubernetes/check-pod-status.go | 25 +- .../kubernetes/create-agnhost-statefulset.go | 1 - .../kubernetes/create-kapinger-deployment.go | 49 +- .../framework/kubernetes/get-external-crd.go | 2 +- .../kubernetes/install-retina-helm.go | 2 +- test/e2e/framework/kubernetes/pprof.go | 51 +- test/e2e/framework/kubernetes/validateHttp.go | 2 +- test/e2e/scenarios/longrunning/scenario.go | 13 +- test/e2e/scenarios/tcp/scenario.go | 2 + 14 files changed, 1335 insertions(+), 53 deletions(-) create mode 100644 deploy/legacy/manifests/controller/helm/retina/crds/monitoring.coreos.com_servicemonitors.yaml create mode 100644 test/e2e/Makefile diff --git a/deploy/legacy/manifests/controller/helm/retina/crds/monitoring.coreos.com_servicemonitors.yaml b/deploy/legacy/manifests/controller/helm/retina/crds/monitoring.coreos.com_servicemonitors.yaml new file mode 100644 index 0000000000..2743e1a9bd --- /dev/null +++ b/deploy/legacy/manifests/controller/helm/retina/crds/monitoring.coreos.com_servicemonitors.yaml @@ -0,0 +1,1216 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + operator.prometheus.io/version: 0.78.2 + name: servicemonitors.monitoring.coreos.com +spec: + group: monitoring.coreos.com + names: + categories: + - prometheus-operator + kind: ServiceMonitor + listKind: ServiceMonitorList + plural: servicemonitors + shortNames: + - smon + singular: servicemonitor + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: |- + The `ServiceMonitor` custom resource definition (CRD) defines how `Prometheus` and `PrometheusAgent` can scrape metrics from a group of services. + Among other things, it allows to specify: + * The services to scrape via label selectors. + * The container ports to scrape. + * Authentication credentials to use. + * Target and metric relabeling. + + `Prometheus` and `PrometheusAgent` objects select `ServiceMonitor` objects using label and namespace selectors. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of desired Service selection for target discovery by + Prometheus. + properties: + attachMetadata: + description: |- + `attachMetadata` defines additional metadata which is added to the + discovered targets. + + It requires Prometheus >= v2.37.0. + properties: + node: + description: |- + When set to true, Prometheus attaches node metadata to the discovered + targets. + + The Prometheus service account must have the `list` and `watch` + permissions on the `Nodes` objects. + type: boolean + type: object + bodySizeLimit: + description: |- + When defined, bodySizeLimit specifies a job level limit on the size + of uncompressed response body that will be accepted by Prometheus. + + It requires Prometheus >= v2.28.0. + pattern: (^0|([0-9]*[.])?[0-9]+((K|M|G|T|E|P)i?)?B)$ + type: string + endpoints: + description: |- + List of endpoints part of this ServiceMonitor. + Defines how to scrape metrics from Kubernetes [Endpoints](https://kubernetes.io/docs/concepts/services-networking/service/#endpoints) objects. + In most cases, an Endpoints object is backed by a Kubernetes [Service](https://kubernetes.io/docs/concepts/services-networking/service/) object with the same name and labels. + items: + description: |- + Endpoint defines an endpoint serving Prometheus metrics to be scraped by + Prometheus. + properties: + authorization: + description: |- + `authorization` configures the Authorization header credentials to use when + scraping the target. + + Cannot be set at the same time as `basicAuth`, or `oauth2`. + properties: + credentials: + description: Selects a key of a Secret in the namespace + that contains the credentials for authentication. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: + description: |- + Defines the authentication type. The value is case-insensitive. + + "Basic" is not a supported value. + + Default: "Bearer" + type: string + type: object + basicAuth: + description: |- + `basicAuth` configures the Basic Authentication credentials to use when + scraping the target. + + Cannot be set at the same time as `authorization`, or `oauth2`. + properties: + password: + description: |- + `password` specifies a key of a Secret containing the password for + authentication. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + description: |- + `username` specifies a key of a Secret containing the username for + authentication. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + description: |- + File to read bearer token for scraping the target. + + Deprecated: use `authorization` instead. + type: string + bearerTokenSecret: + description: |- + `bearerTokenSecret` specifies a key of a Secret containing the bearer + token for scraping targets. The secret needs to be in the same namespace + as the ServiceMonitor object and readable by the Prometheus Operator. + + Deprecated: use `authorization` instead. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + enableHttp2: + description: '`enableHttp2` can be used to disable HTTP2 when + scraping the target.' + type: boolean + filterRunning: + description: |- + When true, the pods which are not running (e.g. either in Failed or + Succeeded state) are dropped during the target discovery. + + If unset, the filtering is enabled. + + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase + type: boolean + followRedirects: + description: |- + `followRedirects` defines whether the scrape requests should follow HTTP + 3xx redirects. + type: boolean + honorLabels: + description: |- + When true, `honorLabels` preserves the metric's labels when they collide + with the target's labels. + type: boolean + honorTimestamps: + description: |- + `honorTimestamps` controls whether Prometheus preserves the timestamps + when exposed by the target. + type: boolean + interval: + description: |- + Interval at which Prometheus scrapes the metrics from the target. + + If empty, Prometheus uses the global scrape interval. + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + metricRelabelings: + description: |- + `metricRelabelings` configures the relabeling rules to apply to the + samples before ingestion. + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set for targets, alerts, + scraped samples and remote write samples. + + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config + properties: + action: + default: replace + description: |- + Action to perform based on the regex matching. + + `Uppercase` and `Lowercase` actions require Prometheus >= v2.36.0. + `DropEqual` and `KeepEqual` actions require Prometheus >= v2.41.0. + + Default: "Replace" + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + description: |- + Modulus to take of the hash of the source label values. + + Only applicable when the action is `HashMod`. + format: int64 + type: integer + regex: + description: Regular expression against which the extracted + value is matched. + type: string + replacement: + description: |- + Replacement value against which a Replace action is performed if the + regular expression matches. + + Regex capture groups are available. + type: string + separator: + description: Separator is the string between concatenated + SourceLabels. + type: string + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is + concatenated using the configured Separator and matched against the + configured regular expression. + items: + description: |- + LabelName is a valid Prometheus label name which may only contain ASCII + letters, numbers, as well as underscores. + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + description: |- + Label to which the resulting string is written in a replacement. + + It is mandatory for `Replace`, `HashMod`, `Lowercase`, `Uppercase`, + `KeepEqual` and `DropEqual` actions. + + Regex capture groups are available. + type: string + type: object + type: array + oauth2: + description: |- + `oauth2` configures the OAuth2 settings to use when scraping the target. + + It requires Prometheus >= 2.27.0. + + Cannot be set at the same time as `authorization`, or `basicAuth`. + properties: + clientId: + description: |- + `clientId` specifies a key of a Secret or ConfigMap containing the + OAuth2 client's ID. + properties: + configMap: + description: ConfigMap containing data to use for the + targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + clientSecret: + description: |- + `clientSecret` specifies a key of a Secret containing the OAuth2 + client's secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + endpointParams: + additionalProperties: + type: string + description: |- + `endpointParams` configures the HTTP parameters to append to the token + URL. + type: object + noProxy: + description: |- + `noProxy` is a comma-separated string that can contain IPs, CIDR notation, domain names + that should be excluded from proxying. IP and domain names can + contain port numbers. + + It requires Prometheus >= v2.43.0 or Alertmanager >= 0.25.0. + type: string + proxyConnectHeader: + additionalProperties: + items: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: array + description: |- + ProxyConnectHeader optionally specifies headers to send to + proxies during CONNECT requests. + + It requires Prometheus >= v2.43.0 or Alertmanager >= 0.25.0. + type: object + x-kubernetes-map-type: atomic + proxyFromEnvironment: + description: |- + Whether to use the proxy configuration defined by environment variables (HTTP_PROXY, HTTPS_PROXY, and NO_PROXY). + + It requires Prometheus >= v2.43.0 or Alertmanager >= 0.25.0. + type: boolean + proxyUrl: + description: '`proxyURL` defines the HTTP proxy server to + use.' + pattern: ^http(s)?://.+$ + type: string + scopes: + description: '`scopes` defines the OAuth2 scopes used for + the token request.' + items: + type: string + type: array + tlsConfig: + description: |- + TLS configuration to use when connecting to the OAuth2 server. + It requires Prometheus >= v2.43.0. + properties: + ca: + description: Certificate authority used when verifying + server certificates. + properties: + configMap: + description: ConfigMap containing data to use for + the targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the + targets. + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + cert: + description: Client certificate to present when doing + client-authentication. + properties: + configMap: + description: ConfigMap containing data to use for + the targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the + targets. + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + insecureSkipVerify: + description: Disable target certificate validation. + type: boolean + keySecret: + description: Secret containing the client key file for + the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + description: |- + Maximum acceptable TLS version. + + It requires Prometheus >= v2.41.0. + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + description: |- + Minimum acceptable TLS version. + + It requires Prometheus >= v2.35.0. + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + description: Used to verify the hostname for the targets. + type: string + type: object + tokenUrl: + description: '`tokenURL` configures the URL to fetch the + token from.' + minLength: 1 + type: string + required: + - clientId + - clientSecret + - tokenUrl + type: object + params: + additionalProperties: + items: + type: string + type: array + description: params define optional HTTP URL parameters. + type: object + path: + description: |- + HTTP path from which to scrape for metrics. + + If empty, Prometheus uses the default value (e.g. `/metrics`). + type: string + port: + description: |- + Name of the Service port which this endpoint refers to. + + It takes precedence over `targetPort`. + type: string + proxyUrl: + description: |- + `proxyURL` configures the HTTP Proxy URL (e.g. + "http://proxyserver:2195") to go through when scraping the target. + type: string + relabelings: + description: |- + `relabelings` configures the relabeling rules to apply the target's + metadata labels. + + The Operator automatically adds relabelings for a few standard Kubernetes fields. + + The original scrape job's name is available via the `__tmp_prometheus_job_name` label. + + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set for targets, alerts, + scraped samples and remote write samples. + + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config + properties: + action: + default: replace + description: |- + Action to perform based on the regex matching. + + `Uppercase` and `Lowercase` actions require Prometheus >= v2.36.0. + `DropEqual` and `KeepEqual` actions require Prometheus >= v2.41.0. + + Default: "Replace" + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + description: |- + Modulus to take of the hash of the source label values. + + Only applicable when the action is `HashMod`. + format: int64 + type: integer + regex: + description: Regular expression against which the extracted + value is matched. + type: string + replacement: + description: |- + Replacement value against which a Replace action is performed if the + regular expression matches. + + Regex capture groups are available. + type: string + separator: + description: Separator is the string between concatenated + SourceLabels. + type: string + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is + concatenated using the configured Separator and matched against the + configured regular expression. + items: + description: |- + LabelName is a valid Prometheus label name which may only contain ASCII + letters, numbers, as well as underscores. + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + description: |- + Label to which the resulting string is written in a replacement. + + It is mandatory for `Replace`, `HashMod`, `Lowercase`, `Uppercase`, + `KeepEqual` and `DropEqual` actions. + + Regex capture groups are available. + type: string + type: object + type: array + scheme: + description: |- + HTTP scheme to use for scraping. + + `http` and `https` are the expected values unless you rewrite the + `__scheme__` label via relabeling. + + If empty, Prometheus uses the default value `http`. + enum: + - http + - https + type: string + scrapeTimeout: + description: |- + Timeout after which Prometheus considers the scrape to be failed. + + If empty, Prometheus uses the global scrape timeout unless it is less + than the target's scrape interval value in which the latter is used. + pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the target port of the `Pod` object behind the + Service. The port must be specified with the container's port property. + x-kubernetes-int-or-string: true + tlsConfig: + description: TLS configuration to use when scraping the target. + properties: + ca: + description: Certificate authority used when verifying server + certificates. + properties: + configMap: + description: ConfigMap containing data to use for the + targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + description: Path to the CA cert in the Prometheus container + to use for the targets. + type: string + cert: + description: Client certificate to present when doing client-authentication. + properties: + configMap: + description: ConfigMap containing data to use for the + targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + description: Path to the client cert file in the Prometheus + container for the targets. + type: string + insecureSkipVerify: + description: Disable target certificate validation. + type: boolean + keyFile: + description: Path to the client key file in the Prometheus + container for the targets. + type: string + keySecret: + description: Secret containing the client key file for the + targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + maxVersion: + description: |- + Maximum acceptable TLS version. + + It requires Prometheus >= v2.41.0. + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + minVersion: + description: |- + Minimum acceptable TLS version. + + It requires Prometheus >= v2.35.0. + enum: + - TLS10 + - TLS11 + - TLS12 + - TLS13 + type: string + serverName: + description: Used to verify the hostname for the targets. + type: string + type: object + trackTimestampsStaleness: + description: |- + `trackTimestampsStaleness` defines whether Prometheus tracks staleness of + the metrics that have an explicit timestamp present in scraped data. + Has no effect if `honorTimestamps` is false. + + It requires Prometheus >= v2.48.0. + type: boolean + type: object + type: array + jobLabel: + description: |- + `jobLabel` selects the label from the associated Kubernetes `Service` + object which will be used as the `job` label for all metrics. + + For example if `jobLabel` is set to `foo` and the Kubernetes `Service` + object is labeled with `foo: bar`, then Prometheus adds the `job="bar"` + label to all ingested metrics. + + If the value of this field is empty or if the label doesn't exist for + the given Service, the `job` label of the metrics defaults to the name + of the associated Kubernetes `Service`. + type: string + keepDroppedTargets: + description: |- + Per-scrape limit on the number of targets dropped by relabeling + that will be kept in memory. 0 means no limit. + + It requires Prometheus >= v2.47.0. + format: int64 + type: integer + labelLimit: + description: |- + Per-scrape limit on number of labels that will be accepted for a sample. + + It requires Prometheus >= v2.27.0. + format: int64 + type: integer + labelNameLengthLimit: + description: |- + Per-scrape limit on length of labels name that will be accepted for a sample. + + It requires Prometheus >= v2.27.0. + format: int64 + type: integer + labelValueLengthLimit: + description: |- + Per-scrape limit on length of labels value that will be accepted for a sample. + + It requires Prometheus >= v2.27.0. + format: int64 + type: integer + namespaceSelector: + description: |- + `namespaceSelector` defines in which namespace(s) Prometheus should discover the services. + By default, the services are discovered in the same namespace as the `ServiceMonitor` object but it is possible to select pods across different/all namespaces. + properties: + any: + description: |- + Boolean describing whether all namespaces are selected in contrast to a + list restricting them. + type: boolean + matchNames: + description: List of namespace names to select from. + items: + type: string + type: array + type: object + nativeHistogramBucketLimit: + description: |- + If there are more than this many buckets in a native histogram, + buckets will be merged to stay within the limit. + It requires Prometheus >= v2.45.0. + format: int64 + type: integer + nativeHistogramMinBucketFactor: + anyOf: + - type: integer + - type: string + description: |- + If the growth factor of one bucket to the next is smaller than this, + buckets will be merged to increase the factor sufficiently. + It requires Prometheus >= v2.50.0. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + podTargetLabels: + description: |- + `podTargetLabels` defines the labels which are transferred from the + associated Kubernetes `Pod` object onto the ingested metrics. + items: + type: string + type: array + sampleLimit: + description: |- + `sampleLimit` defines a per-scrape limit on the number of scraped samples + that will be accepted. + format: int64 + type: integer + scrapeClass: + description: The scrape class to apply. + minLength: 1 + type: string + scrapeClassicHistograms: + description: |- + Whether to scrape a classic histogram that is also exposed as a native histogram. + It requires Prometheus >= v2.45.0. + type: boolean + scrapeFallbackProtocol: + description: |- + The protocol to use if a scrape returns blank, unparseable, or otherwise invalid Content-Type. + + It requires Prometheus >= v3.0.0. + enum: + - PrometheusProto + - OpenMetricsText0.0.1 + - OpenMetricsText1.0.0 + - PrometheusText0.0.4 + - PrometheusText1.0.0 + type: string + scrapeProtocols: + description: |- + `scrapeProtocols` defines the protocols to negotiate during a scrape. It tells clients the + protocols supported by Prometheus in order of preference (from most to least preferred). + + If unset, Prometheus uses its default value. + + It requires Prometheus >= v2.49.0. + items: + description: |- + ScrapeProtocol represents a protocol used by Prometheus for scraping metrics. + Supported values are: + * `OpenMetricsText0.0.1` + * `OpenMetricsText1.0.0` + * `PrometheusProto` + * `PrometheusText0.0.4` + * `PrometheusText1.0.0` + enum: + - PrometheusProto + - OpenMetricsText0.0.1 + - OpenMetricsText1.0.0 + - PrometheusText0.0.4 + - PrometheusText1.0.0 + type: string + type: array + x-kubernetes-list-type: set + selector: + description: Label selector to select the Kubernetes `Endpoints` objects + to scrape metrics from. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + targetLabels: + description: |- + `targetLabels` defines the labels which are transferred from the + associated Kubernetes `Service` object onto the ingested metrics. + items: + type: string + type: array + targetLimit: + description: |- + `targetLimit` defines a limit on the number of scraped targets that will + be accepted. + format: int64 + type: integer + required: + - endpoints + - selector + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/hack/tools/kapinger/clients/http.go b/hack/tools/kapinger/clients/http.go index 471fe2f00e..f147bcdd8a 100644 --- a/hack/tools/kapinger/clients/http.go +++ b/hack/tools/kapinger/clients/http.go @@ -21,7 +21,8 @@ const ( Service TargetType = "service" Pod TargetType = "pod" - envTargetType = "TARGET_TYPE" + envTargetType = "TARGET_TYPE" + defaultHTTPClientTimeout = 30 * time.Second ) type KapingerHTTPClient struct { @@ -41,7 +42,7 @@ func NewKapingerHTTPClient(clientset *kubernetes.Clientset, labelselector string Transport: &http.Transport{ DisableKeepAlives: true, }, - Timeout: 3 * time.Second, + Timeout: defaultHTTPClientTimeout, }, labelselector: labelselector, clientset: clientset, diff --git a/hack/tools/kapinger/servers/http.go b/hack/tools/kapinger/servers/http.go index 5810c5e4d5..ce8d029442 100644 --- a/hack/tools/kapinger/servers/http.go +++ b/hack/tools/kapinger/servers/http.go @@ -22,7 +22,7 @@ func (k *KapingerHTTPServer) Start(ctx context.Context) error { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := w.Write(getResponse(r.RemoteAddr, "http")) if err != nil { - fmt.Println(err) + fmt.Printf("http server: error writing response: %v\n", err) } }) diff --git a/hack/tools/kapinger/servers/util.go b/hack/tools/kapinger/servers/util.go index 497c90071e..d81662b5a0 100644 --- a/hack/tools/kapinger/servers/util.go +++ b/hack/tools/kapinger/servers/util.go @@ -7,5 +7,5 @@ import ( func getResponse(addressString, protocol string) []byte { podname := os.Getenv("POD_NAME") - return []byte(fmt.Sprintf("connected to: %s via %s, connected from: %v", podname, protocol, addressString)) + return []byte(fmt.Sprintf("connected to: %s via %s, connected from: %v\n", podname, protocol, addressString)) } diff --git a/test/e2e/Makefile b/test/e2e/Makefile new file mode 100644 index 0000000000..9bc1e8ee2d --- /dev/null +++ b/test/e2e/Makefile @@ -0,0 +1,16 @@ + +.PHONY: longrunning +longrunning: + @echo "Running long running tests" + @echo "Be sure to set CLUSTER_NAME, TAG, AZURE_LOCATION, AZURE_SUBSCRIPTION_ID" + @if [ -z "$$CLUSTER_NAME" ]; then echo "CLUSTER_NAME is not set"; exit 1; fi + @if [ -z "$$AZURE_LOCATION" ]; then echo "AZURE_LOCATION is not set"; exit 1; fi + @if [ -z "$$AZURE_SUBSCRIPTION_ID" ]; then echo "AZURE_SUBSCRIPTION_ID is not set"; exit 1; fi + @if [ -z "$$TAG" ]; then echo "TAG is not set"; exit 1; fi + go test -v ./*.go -timeout 0 -tags=e2e -count=1 \ + -run=TestLongRunningRetina \ + -args \ + -create-infra=false \ + -delete-infra=false \ + -image-namespace=microsoft/retina \ + -image-registry=acnpublic.azurecr.io diff --git a/test/e2e/framework/kubernetes/check-pod-status.go b/test/e2e/framework/kubernetes/check-pod-status.go index 27405031bb..d2cfe0408b 100644 --- a/test/e2e/framework/kubernetes/check-pod-status.go +++ b/test/e2e/framework/kubernetes/check-pod-status.go @@ -37,7 +37,6 @@ func (w *WaitPodsReady) Prevalidate() error { // Primary step where test logic is executed // Returning an error will cause the test to fail func (w *WaitPodsReady) Run() error { - config, err := clientcmd.BuildConfigFromFlags("", w.KubeConfigFilePath) if err != nil { return fmt.Errorf("error building kubeconfig: %w", err) @@ -60,8 +59,6 @@ func (w *WaitPodsReady) Stop() error { } func WaitForPodReady(ctx context.Context, clientset *kubernetes.Clientset, namespace, labelSelector string) error { - podReadyMap := make(map[string]bool) - printIterator := 0 conditionFunc := wait.ConditionWithContextFunc(func(context.Context) (bool, error) { defer func() { @@ -80,10 +77,12 @@ func WaitForPodReady(ctx context.Context, clientset *kubernetes.Clientset, names // check each indviidual pod to see if it's in Running state for i := range podList.Items { - var pod *corev1.Pod - pod, err = clientset.CoreV1().Pods(namespace).Get(ctx, podList.Items[i].Name, metav1.GetOptions{}) - if err != nil { - return false, fmt.Errorf("error getting Pod: %w", err) + pod := &podList.Items[i] + for istatus := range pod.Status.ContainerStatuses { + status := &pod.Status.ContainerStatuses[istatus] + if status.RestartCount > 0 { + return false, fmt.Errorf("pod %s has %d restarts: status: %+v: %w", pod.Name, status.RestartCount, status, ErrPodCrashed) + } } // Check the Pod phase @@ -94,18 +93,6 @@ func WaitForPodReady(ctx context.Context, clientset *kubernetes.Clientset, names return false, nil } - // Check all container status. - for _, containerStatus := range pod.Status.ContainerStatuses { - if !containerStatus.Ready { - log.Printf("container \"%s\" in pod \"%s\" is not ready yet. Waiting...\n", containerStatus.Name, pod.Name) - return false, nil - } - } - - if !podReadyMap[pod.Name] { - log.Printf("pod \"%s\" is in Running state\n", pod.Name) - podReadyMap[pod.Name] = true - } } log.Printf("all pods in namespace \"%s\" with label \"%s\" are in Running state\n", namespace, labelSelector) return true, nil diff --git a/test/e2e/framework/kubernetes/create-agnhost-statefulset.go b/test/e2e/framework/kubernetes/create-agnhost-statefulset.go index 6783d7aa66..8f071ee333 100644 --- a/test/e2e/framework/kubernetes/create-agnhost-statefulset.go +++ b/test/e2e/framework/kubernetes/create-agnhost-statefulset.go @@ -90,7 +90,6 @@ func (c *CreateAgnhostStatefulSet) getAgnhostDeployment() *appsv1.StatefulSet { }, }, } - } else { affinity = &v1.Affinity{ PodAntiAffinity: &v1.PodAntiAffinity{ diff --git a/test/e2e/framework/kubernetes/create-kapinger-deployment.go b/test/e2e/framework/kubernetes/create-kapinger-deployment.go index 06862e1c09..79a9ddec80 100644 --- a/test/e2e/framework/kubernetes/create-kapinger-deployment.go +++ b/test/e2e/framework/kubernetes/create-kapinger-deployment.go @@ -27,6 +27,8 @@ type CreateKapingerDeployment struct { KapingerNamespace string KapingerReplicas string KubeConfigFilePath string + BurstIntervalMs string + BurstVolume string } func (c *CreateKapingerDeployment) Run() error { @@ -63,6 +65,11 @@ func (c *CreateKapingerDeployment) Run() error { } } + err = WaitForPodReady(ctx, clientset, c.KapingerNamespace, "app=kapinger") + if err != nil { + return fmt.Errorf("error waiting for agnhost pod to be ready: %w", err) + } + return nil } @@ -92,6 +99,13 @@ func (c *CreateKapingerDeployment) GetKapingerDeployment() *appsv1.Deployment { Namespace: c.KapingerNamespace, }, Spec: appsv1.DeploymentSpec{ + Strategy: appsv1.DeploymentStrategy{ + Type: appsv1.RollingUpdateDeploymentStrategyType, + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{IntVal: 20}, + MaxSurge: &intstr.IntOrString{IntVal: 20}, + }, + }, Replicas: &reps, Selector: &metaV1.LabelSelector{ MatchLabels: map[string]string{ @@ -148,8 +162,20 @@ func (c *CreateKapingerDeployment) GetKapingerDeployment() *appsv1.Deployment { }, Env: []v1.EnvVar{ { - Name: "GODEBUG", - Value: "netdns=go", + Name: "POD_NAME", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "POD_IP", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "status.podIP", + }, + }, }, { Name: "TARGET_TYPE", @@ -167,6 +193,25 @@ func (c *CreateKapingerDeployment) GetKapingerDeployment() *appsv1.Deployment { Name: "UDP_PORT", Value: strconv.Itoa(KapingerUDPPort), }, + { + Name: "BURST_INTERVAL_MS", + Value: c.BurstIntervalMs, + }, + { + Name: "BURST_VOLUME", + Value: c.BurstVolume, + }, + }, + LivenessProbe: &v1.Probe{ + ProbeHandler: v1.ProbeHandler{ + HTTPGet: &v1.HTTPGetAction{ + Path: "/", + Port: intstr.FromInt(KapingerHTTPPort), + }, + }, + TimeoutSeconds: 10, //nolint + PeriodSeconds: 10, //nolint + InitialDelaySeconds: 3, //nolint }, }, }, diff --git a/test/e2e/framework/kubernetes/get-external-crd.go b/test/e2e/framework/kubernetes/get-external-crd.go index 1564691362..6e80e226ab 100644 --- a/test/e2e/framework/kubernetes/get-external-crd.go +++ b/test/e2e/framework/kubernetes/get-external-crd.go @@ -55,7 +55,7 @@ func extractFileName(rawURL string) (string, error) { } func saveToFile(filename string, data []byte) error { - err := os.WriteFile(filename, data, 0644) + err := os.WriteFile(filename, data, 0o644) if err != nil { return fmt.Errorf("failed to write crd.yaml to /crds dir : %w", err) } diff --git a/test/e2e/framework/kubernetes/install-retina-helm.go b/test/e2e/framework/kubernetes/install-retina-helm.go index 7f1828f17c..deb211fd31 100644 --- a/test/e2e/framework/kubernetes/install-retina-helm.go +++ b/test/e2e/framework/kubernetes/install-retina-helm.go @@ -67,7 +67,7 @@ func (i *InstallHelmChart) Run() error { return fmt.Errorf("image namespace is not set: %w", errEmpty) } - //Download necessary CRD's + // Download necessary CRD's err = downloadExternalCRDs(i.ChartPath) if err != nil { return fmt.Errorf("failed to load external crd's: %w", err) diff --git a/test/e2e/framework/kubernetes/pprof.go b/test/e2e/framework/kubernetes/pprof.go index 56451488a8..57ec06a65f 100644 --- a/test/e2e/framework/kubernetes/pprof.go +++ b/test/e2e/framework/kubernetes/pprof.go @@ -9,6 +9,7 @@ import ( "net/url" "os" "strconv" + "sync" "time" ) @@ -22,9 +23,11 @@ var ( // key:profile name, value: pprof endpoint simpleProfiles = map[string]string{ - "heap": "/heap", - "block": "/block", - "mutex": "/mutex", + "heap": "/heap", + "block": "/block", + "mutex": "/mutex", + "cmdline": "/cmdline", + "symbol": "/symbol", } durationProfiles = map[string]string{ @@ -59,20 +62,21 @@ func (p *PullPProf) Run() error { return fmt.Errorf("error converting pprof interval to int: %w", err) } + log.Printf("starting pprof scraping for %s seconds at %s second intervals\n", p.DurationSeconds, p.ScrapeIntervalSeconds) + ticker := time.NewTicker(time.Duration(interval) * time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Duration(duration)*time.Second) defer cancel() - defer func() { - if err != nil { - log.Printf("--pprof viewing commands:--\n") - log.Printf("heap: \tgo tool pprof -http=:8081 %s\n", "heap.out") - log.Printf("cpu: \tgo tool pprof -http=:8082 %s\n", "cpu.out") - log.Printf("block: \tgo tool pprof -http=:8083 %s\n", "block.out") - log.Printf("mutex: \tgo tool pprof -http=:8084 %s\n", "mutex.out") - log.Printf("trace: \tgo tool trace -http=:8085 %s\n", "trace.out") - } - }() + log.Printf("--pprof viewing commands:--\n") + log.Printf("profile: \tgo tool pprof -http=:8081 %s\n", "profile.out") + log.Printf("heap: \tgo tool pprof -http=:8081 %s\n", "heap.out") + log.Printf("cpu: \tgo tool pprof -http=:8082 %s\n", "cpu.out") + log.Printf("block: \tgo tool pprof -http=:8083 %s\n", "block.out") + log.Printf("mutex: \tgo tool pprof -http=:8084 %s\n", "mutex.out") + log.Printf("cmdline: \tgo tool pprof -http=:8085 %s\n", "cmdline.out") + log.Printf("symbol: \tgo tool pprof -http=:8086 %s\n", "symbol.out") + log.Printf("trace: \tgo tool trace -http=:8087 %s\n", "trace.out") scrape := func() error { log.Printf("-- starting scrape pprof profiles --\n") @@ -92,15 +96,22 @@ func (p *PullPProf) Run() error { } } + var wg sync.WaitGroup + for name, path := range durationProfiles { - file := folder + name + ".out" - err = p.scraper.GetProfileWithDuration(name, path, file, defaultSpanTime) - if err != nil { - // don't return here because some data is better than no data, - // and other profiles might be functional - log.Printf("error getting %s profile: %v\n", name, err) - } + wg.Add(1) + go func(name, path string) { + file := folder + name + ".out" + err = p.scraper.GetProfileWithDuration(name, path, file, defaultSpanTime) + if err != nil { + // don't return here because some data is better than no data, + // and other profiles might be functional + log.Printf("error getting %s profile: %v\n", name, err) + } + wg.Done() + }(name, path) } + wg.Wait() log.Printf("-- finished scraping profiles, saved to to %s --\n", folder) log.Printf("waiting %s seconds for next scrape\n", p.ScrapeIntervalSeconds) diff --git a/test/e2e/framework/kubernetes/validateHttp.go b/test/e2e/framework/kubernetes/validateHttp.go index 39e2e400b2..8400647714 100644 --- a/test/e2e/framework/kubernetes/validateHttp.go +++ b/test/e2e/framework/kubernetes/validateHttp.go @@ -21,7 +21,7 @@ func (v *ValidateHTTPResponse) Run() error { ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout) defer cancel() - req, err := http.NewRequestWithContext(ctx, http.MethodGet, v.URL, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, v.URL, http.NoBody) if err != nil { return fmt.Errorf("error creating HTTP request: %w", err) } diff --git a/test/e2e/scenarios/longrunning/scenario.go b/test/e2e/scenarios/longrunning/scenario.go index e685543864..8bbd7b80fa 100644 --- a/test/e2e/scenarios/longrunning/scenario.go +++ b/test/e2e/scenarios/longrunning/scenario.go @@ -1,6 +1,9 @@ package longrunning import ( + "strconv" + "time" + "github.com/microsoft/retina/test/e2e/framework/kubernetes" "github.com/microsoft/retina/test/e2e/framework/types" ) @@ -10,9 +13,11 @@ func PullPProf(kubeConfigFilePath string) *types.Scenario { Steps := []*types.StepWrapper{ { Step: &kubernetes.CreateKapingerDeployment{ - KapingerNamespace: "kube-system", - KapingerReplicas: "5", + KapingerNamespace: "default", + KapingerReplicas: "500", KubeConfigFilePath: kubeConfigFilePath, + BurstIntervalMs: "10000", // 10 seconds + BurstVolume: "10", // 500 requests every 10 seconds }, }, { @@ -30,8 +35,8 @@ func PullPProf(kubeConfigFilePath string) *types.Scenario { }, { Step: &kubernetes.PullPProf{ - DurationSeconds: "28800", // 8 hours - ScrapeIntervalSeconds: "60", + DurationSeconds: strconv.Itoa(int((8 * time.Hour).Seconds())), //nolint part of the test + ScrapeIntervalSeconds: strconv.Itoa(int((1 * time.Hour).Seconds())), //nolint part of the test }, }, { diff --git a/test/e2e/scenarios/tcp/scenario.go b/test/e2e/scenarios/tcp/scenario.go index 0afa0c5013..6a8052f5d0 100644 --- a/test/e2e/scenarios/tcp/scenario.go +++ b/test/e2e/scenarios/tcp/scenario.go @@ -23,6 +23,8 @@ func ValidateTCPMetrics(namespace string) *types.Scenario { Step: &kubernetes.CreateKapingerDeployment{ KapingerNamespace: namespace, KapingerReplicas: "1", + BurstIntervalMs: "10000", // 10 seconds + BurstVolume: "10", // 500 requests every 10 seconds }, }, { From 5a153d059a493c6d24edd08339d3372e3dfef0bf Mon Sep 17 00:00:00 2001 From: Mathew Merrick Date: Fri, 11 Oct 2024 23:08:02 +0000 Subject: [PATCH 4/6] retry on apiserver --- hack/tools/kapinger/clients/http.go | 80 ++++++++++++++++--- hack/tools/kapinger/go.mod | 25 +++--- hack/tools/kapinger/go.sum | 34 ++++++++ .../kubernetes/create-kapinger-deployment.go | 8 ++ test/prometheus/create-cm.sh | 2 +- 5 files changed, 124 insertions(+), 25 deletions(-) diff --git a/hack/tools/kapinger/clients/http.go b/hack/tools/kapinger/clients/http.go index f147bcdd8a..027e073c45 100644 --- a/hack/tools/kapinger/clients/http.go +++ b/hack/tools/kapinger/clients/http.go @@ -23,6 +23,9 @@ const ( envTargetType = "TARGET_TYPE" defaultHTTPClientTimeout = 30 * time.Second + + defaultRetryAttempts = 10 + defaultRetryDelay = 5 * time.Second ) type KapingerHTTPClient struct { @@ -73,7 +76,7 @@ func NewKapingerHTTPClient(clientset *kubernetes.Clientset, labelselector string return nil, fmt.Errorf("env TARGET_TYPE must be \"service\" or \"pod\"") } if err != nil { - return nil, fmt.Errorf("error getting IPs: %w", err) + return nil, err } return &k, nil @@ -103,10 +106,30 @@ func (k *KapingerHTTPClient) MakeRequests(ctx context.Context) error { } } -func (k *KapingerHTTPClient) makeRequest(ctx context.Context, url string) ([]byte, error) { - req, err := http.NewRequestWithContext(ctx, "GET", url, http.NoBody) - if err != nil { - return nil, err +func (k *KapingerHTTPClient) makeRequest() error { + for _, ip := range k.ips { + url := fmt.Sprintf("http://%s:%d", ip, k.port) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + // Set the "Connection" header to "close" + req.Header.Set("Connection", "close") + + // Send the request + resp, err := k.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatalf("http client: error reading response body from %s: %v", url, err) + return err + } + log.Printf("http client: response from %s: %s", url, string(body)) } // Set the "Connection" header to "close" @@ -128,13 +151,46 @@ func (k *KapingerHTTPClient) makeRequest(ctx context.Context, url string) ([]byt return body, nil } -func (k *KapingerHTTPClient) getServiceURLs() ([]string, error) { - urls := []string{} - services, err := k.clientset.CoreV1().Services(corev1.NamespaceAll).List(context.Background(), metav1.ListOptions{ - LabelSelector: k.labelselector, - }) - if err != nil { - return urls, fmt.Errorf("error getting services: %w", err) +func (k *KapingerHTTPClient) getIPS() error { + ips := []string{} + + switch k.targettype { + case Service: + services, err := k.clientset.CoreV1().Services(corev1.NamespaceAll).List(context.Background(), metav1.ListOptions{ + LabelSelector: k.labelselector, + }) + if err != nil { + return fmt.Errorf("http client: error getting services: %w", err) + } + + // Extract the Service cluster IP addresses + + for _, svc := range services.Items { + ips = append(ips, svc.Spec.ClusterIP) + } + log.Println("http client: using service IPs:", ips) + + case Pod: + err := waitForPodsRunning(k.clientset, k.labelselector) + if err != nil { + return fmt.Errorf("http client: error waiting for pods to be in Running state: %w", err) + } + + // Get all pods in the cluster with label app=agnhost + pods, err := k.clientset.CoreV1().Pods(corev1.NamespaceAll).List(context.Background(), metav1.ListOptions{ + LabelSelector: k.labelselector, + }) + if err != nil { + return fmt.Errorf("http client: error getting pods: %w", err) + } + + for _, pod := range pods.Items { + ips = append(ips, pod.Status.PodIP) + } + + log.Printf("using pod IPs: %v", ips) + default: + return fmt.Errorf("env TARGET_TYPE must be \"service\" or \"pod\"") } // Extract the Service cluster IP addresses diff --git a/hack/tools/kapinger/go.mod b/hack/tools/kapinger/go.mod index 1d3e1c7422..a2461a784b 100644 --- a/hack/tools/kapinger/go.mod +++ b/hack/tools/kapinger/go.mod @@ -3,23 +3,24 @@ module github.com/microsoft/retina/hack/tools/kapinger go 1.22.5 require ( + github.com/microsoft/retina v0.10.0 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 k8s.io/client-go v0.30.2 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.2 // indirect github.com/go-logr/logr v1.4.1 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/swag v0.22.10 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -32,16 +33,16 @@ 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 - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.34.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 k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/hack/tools/kapinger/go.sum b/hack/tools/kapinger/go.sum index d5d665ee17..f6c65c2715 100644 --- a/hack/tools/kapinger/go.sum +++ b/hack/tools/kapinger/go.sum @@ -2,16 +2,26 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= +github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= +github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA= +github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -21,6 +31,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= +github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -31,6 +43,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -46,6 +60,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/microsoft/retina v0.10.0 h1:LBFqEidd3oJZ0cKnn16wj0gLwYUcZEwEXoWVNrsWOJ0= +github.com/microsoft/retina v0.10.0/go.mod h1:rfXc+UqWj1qSRE5hZw8JuFGl3ILxO6/CIyhQoAmwd0s= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -86,8 +102,12 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -98,8 +118,12 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -107,6 +131,8 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -119,8 +145,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= +google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -144,9 +174,13 @@ k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7F k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= +k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/test/e2e/framework/kubernetes/create-kapinger-deployment.go b/test/e2e/framework/kubernetes/create-kapinger-deployment.go index 79a9ddec80..bab0eab0f7 100644 --- a/test/e2e/framework/kubernetes/create-kapinger-deployment.go +++ b/test/e2e/framework/kubernetes/create-kapinger-deployment.go @@ -177,6 +177,14 @@ func (c *CreateKapingerDeployment) GetKapingerDeployment() *appsv1.Deployment { }, }, }, + { + Name: "GOMEMLIMIT", + ValueFrom: &v1.EnvVarSource{ + ResourceFieldRef: &v1.ResourceFieldSelector{ + Resource: "limits.memory", + }, + }, + }, { Name: "TARGET_TYPE", Value: "service", diff --git a/test/prometheus/create-cm.sh b/test/prometheus/create-cm.sh index eb97c9befe..456e671606 100755 --- a/test/prometheus/create-cm.sh +++ b/test/prometheus/create-cm.sh @@ -2,7 +2,7 @@ kubectl apply -f ./test/prometheus/service.yaml kubectl delete configmap ama-metrics-prometheus-config-node -n kube-system -#kubectl create configmap ama-metrics-prometheus-config-node --from-file=./test/prometheus/ama-metrics-node/prometheus-config -n kube-system +kubectl create configmap ama-metrics-prometheus-config-node --from-file=./test/prometheus/ama-metrics-node/prometheus-config -n kube-system kubectl delete configmap ama-metrics-prometheus-config -n kube-system # kubectl create configmap ama-metrics-prometheus-config --from-file=./test/prometheus/ama-metrics/prometheus-config -n kube-system From a44264665d8e76d5b2d8992c42f50d70af185620 Mon Sep 17 00:00:00 2001 From: Mathew Merrick Date: Mon, 14 Oct 2024 20:28:57 +0000 Subject: [PATCH 5/6] stop chan in port forwarder --- .../kubernetes/create-kapinger-deployment.go | 11 ------ test/e2e/framework/kubernetes/port-forward.go | 4 +++ test/e2e/framework/kubernetes/portforward.go | 3 ++ test/e2e/framework/kubernetes/pprof.go | 34 +++++++------------ test/e2e/scenarios/longrunning/scenario.go | 2 +- 5 files changed, 20 insertions(+), 34 deletions(-) diff --git a/test/e2e/framework/kubernetes/create-kapinger-deployment.go b/test/e2e/framework/kubernetes/create-kapinger-deployment.go index bab0eab0f7..248b29c5ca 100644 --- a/test/e2e/framework/kubernetes/create-kapinger-deployment.go +++ b/test/e2e/framework/kubernetes/create-kapinger-deployment.go @@ -210,17 +210,6 @@ func (c *CreateKapingerDeployment) GetKapingerDeployment() *appsv1.Deployment { Value: c.BurstVolume, }, }, - LivenessProbe: &v1.Probe{ - ProbeHandler: v1.ProbeHandler{ - HTTPGet: &v1.HTTPGetAction{ - Path: "/", - Port: intstr.FromInt(KapingerHTTPPort), - }, - }, - TimeoutSeconds: 10, //nolint - PeriodSeconds: 10, //nolint - InitialDelaySeconds: 3, //nolint - }, }, }, }, diff --git a/test/e2e/framework/kubernetes/port-forward.go b/test/e2e/framework/kubernetes/port-forward.go index db04368372..60180e7ff5 100644 --- a/test/e2e/framework/kubernetes/port-forward.go +++ b/test/e2e/framework/kubernetes/port-forward.go @@ -110,6 +110,8 @@ func (p *PortForward) Run() error { log.Printf("port forward validation HTTP request to \"%s\" succeeded, response: %s\n", p.pf.Address(), resp.Status) + log.Printf("starting keepalive for port forward...\n") + go p.pf.KeepAlive(pctx) return nil } @@ -117,6 +119,8 @@ func (p *PortForward) Run() error { return fmt.Errorf("could not start port forward within %ds: %w", defaultTimeoutSeconds, err) } log.Printf("successfully port forwarded to \"%s\"\n", p.pf.Address()) + log.Printf("starting port forward keepalive...\n") + go p.pf.KeepAlive(pctx) return nil } diff --git a/test/e2e/framework/kubernetes/portforward.go b/test/e2e/framework/kubernetes/portforward.go index a62728d2c3..d5376d6f8b 100644 --- a/test/e2e/framework/kubernetes/portforward.go +++ b/test/e2e/framework/kubernetes/portforward.go @@ -171,6 +171,9 @@ func (p *PortForwarder) KeepAlive(ctx context.Context) { case <-ctx.Done(): p.logger.Logf("port forwarder: keep alive cancelled: %v", ctx.Err()) return + case <-p.stopChan: + p.logger.Logf("port forwarder: keep alive stopped via stop channel") + return case pfErr := <-p.errChan: // as of client-go v0.26.1, if the connection is successful at first but then fails, // an error is logged but only a nil error is sent to this channel. this will be fixed diff --git a/test/e2e/framework/kubernetes/pprof.go b/test/e2e/framework/kubernetes/pprof.go index 57ec06a65f..a85226fbfc 100644 --- a/test/e2e/framework/kubernetes/pprof.go +++ b/test/e2e/framework/kubernetes/pprof.go @@ -9,12 +9,11 @@ import ( "net/url" "os" "strconv" - "sync" "time" ) const ( - defaultTimeout = 30 * time.Second + defaultTimeout = 3200 * time.Second defaultRetinaPort = 10093 defaultSpanTime = 10 * time.Second ) @@ -96,22 +95,15 @@ func (p *PullPProf) Run() error { } } - var wg sync.WaitGroup - for name, path := range durationProfiles { - wg.Add(1) - go func(name, path string) { - file := folder + name + ".out" - err = p.scraper.GetProfileWithDuration(name, path, file, defaultSpanTime) - if err != nil { - // don't return here because some data is better than no data, - // and other profiles might be functional - log.Printf("error getting %s profile: %v\n", name, err) - } - wg.Done() - }(name, path) + file := folder + name + ".out" + err = p.scraper.GetProfileWithDuration(name, path, file, defaultSpanTime) + if err != nil { + // don't return here because some data is better than no data, + // and other profiles might be functional + log.Printf("error getting %s profile: %v\n", name, err) + } } - wg.Wait() log.Printf("-- finished scraping profiles, saved to to %s --\n", folder) log.Printf("waiting %s seconds for next scrape\n", p.ScrapeIntervalSeconds) @@ -166,12 +158,12 @@ func (p *PprofScraper) GetProfileWithDuration(name, path, outfile string, durati log.Printf("getting %s profile for %d seconds...\n", name, seconds) profileURL := p.formatURLWithSeconds(seconds) profileURL.Path += path - return p.scrape(profileURL.String(), defaultTimeout+duration, outfile) + return p.scrape(profileURL.String(), outfile) } func (p *PprofScraper) GetProfile(name, path, outfile string) error { log.Printf("getting %s profile...\n", name) - return p.scrape(p.baseURL.String()+path, defaultTimeout, outfile) + return p.scrape(p.baseURL.String()+path, outfile) } func (p *PprofScraper) formatURLWithSeconds(seconds int) url.URL { @@ -183,10 +175,8 @@ func (p *PprofScraper) formatURLWithSeconds(seconds int) url.URL { return queryURL } -func (p *PprofScraper) scrape(scrapingURL string, timeout time.Duration, outfile string) error { - client := http.Client{ - Timeout: timeout, - } +func (p *PprofScraper) scrape(scrapingURL, outfile string) error { + client := http.Client{} req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, scrapingURL, http.NoBody) if err != nil { diff --git a/test/e2e/scenarios/longrunning/scenario.go b/test/e2e/scenarios/longrunning/scenario.go index 8bbd7b80fa..938a95649d 100644 --- a/test/e2e/scenarios/longrunning/scenario.go +++ b/test/e2e/scenarios/longrunning/scenario.go @@ -17,7 +17,7 @@ func PullPProf(kubeConfigFilePath string) *types.Scenario { KapingerReplicas: "500", KubeConfigFilePath: kubeConfigFilePath, BurstIntervalMs: "10000", // 10 seconds - BurstVolume: "10", // 500 requests every 10 seconds + BurstVolume: "200", // 500 requests every 10 seconds }, }, { From 0d50b96f989a855551f571f46fb55a5218799940 Mon Sep 17 00:00:00 2001 From: Mathew Merrick Date: Mon, 2 Dec 2024 22:25:50 +0000 Subject: [PATCH 6/6] fix lints --- ...monitoring.coreos.com_servicemonitors.yaml | 1216 ----------------- hack/tools/kapinger/clients/http.go | 85 +- .../framework/kubernetes/check-pod-status.go | 25 +- test/e2e/framework/kubernetes/validateHttp.go | 2 +- .../framework/scaletest/add-shared-labels.go | 1 - .../framework/scaletest/add-unique-labels.go | 1 - .../scaletest/create-network-policies.go | 1 - .../scaletest/delete-and-re-add-labels.go | 3 - .../scaletest/get-publish-metrics.go | 7 +- .../scaletest/templates/networkpolicy.go | 34 +- .../framework/scaletest/validate-options.go | 3 +- test/e2e/jobs/jobs.go | 1 - test/e2e/jobs/scale.go | 2 +- test/e2e/retina_longrunning_test.go | 12 +- .../scenarios/perf/publish-perf-results.go | 2 +- test/e2e/settings_test.go | 23 +- 16 files changed, 78 insertions(+), 1340 deletions(-) delete mode 100644 deploy/legacy/manifests/controller/helm/retina/crds/monitoring.coreos.com_servicemonitors.yaml diff --git a/deploy/legacy/manifests/controller/helm/retina/crds/monitoring.coreos.com_servicemonitors.yaml b/deploy/legacy/manifests/controller/helm/retina/crds/monitoring.coreos.com_servicemonitors.yaml deleted file mode 100644 index 2743e1a9bd..0000000000 --- a/deploy/legacy/manifests/controller/helm/retina/crds/monitoring.coreos.com_servicemonitors.yaml +++ /dev/null @@ -1,1216 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.5 - operator.prometheus.io/version: 0.78.2 - name: servicemonitors.monitoring.coreos.com -spec: - group: monitoring.coreos.com - names: - categories: - - prometheus-operator - kind: ServiceMonitor - listKind: ServiceMonitorList - plural: servicemonitors - shortNames: - - smon - singular: servicemonitor - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: |- - The `ServiceMonitor` custom resource definition (CRD) defines how `Prometheus` and `PrometheusAgent` can scrape metrics from a group of services. - Among other things, it allows to specify: - * The services to scrape via label selectors. - * The container ports to scrape. - * Authentication credentials to use. - * Target and metric relabeling. - - `Prometheus` and `PrometheusAgent` objects select `ServiceMonitor` objects using label and namespace selectors. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - Specification of desired Service selection for target discovery by - Prometheus. - properties: - attachMetadata: - description: |- - `attachMetadata` defines additional metadata which is added to the - discovered targets. - - It requires Prometheus >= v2.37.0. - properties: - node: - description: |- - When set to true, Prometheus attaches node metadata to the discovered - targets. - - The Prometheus service account must have the `list` and `watch` - permissions on the `Nodes` objects. - type: boolean - type: object - bodySizeLimit: - description: |- - When defined, bodySizeLimit specifies a job level limit on the size - of uncompressed response body that will be accepted by Prometheus. - - It requires Prometheus >= v2.28.0. - pattern: (^0|([0-9]*[.])?[0-9]+((K|M|G|T|E|P)i?)?B)$ - type: string - endpoints: - description: |- - List of endpoints part of this ServiceMonitor. - Defines how to scrape metrics from Kubernetes [Endpoints](https://kubernetes.io/docs/concepts/services-networking/service/#endpoints) objects. - In most cases, an Endpoints object is backed by a Kubernetes [Service](https://kubernetes.io/docs/concepts/services-networking/service/) object with the same name and labels. - items: - description: |- - Endpoint defines an endpoint serving Prometheus metrics to be scraped by - Prometheus. - properties: - authorization: - description: |- - `authorization` configures the Authorization header credentials to use when - scraping the target. - - Cannot be set at the same time as `basicAuth`, or `oauth2`. - properties: - credentials: - description: Selects a key of a Secret in the namespace - that contains the credentials for authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: |- - Defines the authentication type. The value is case-insensitive. - - "Basic" is not a supported value. - - Default: "Bearer" - type: string - type: object - basicAuth: - description: |- - `basicAuth` configures the Basic Authentication credentials to use when - scraping the target. - - Cannot be set at the same time as `authorization`, or `oauth2`. - properties: - password: - description: |- - `password` specifies a key of a Secret containing the password for - authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: |- - `username` specifies a key of a Secret containing the username for - authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenFile: - description: |- - File to read bearer token for scraping the target. - - Deprecated: use `authorization` instead. - type: string - bearerTokenSecret: - description: |- - `bearerTokenSecret` specifies a key of a Secret containing the bearer - token for scraping targets. The secret needs to be in the same namespace - as the ServiceMonitor object and readable by the Prometheus Operator. - - Deprecated: use `authorization` instead. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - enableHttp2: - description: '`enableHttp2` can be used to disable HTTP2 when - scraping the target.' - type: boolean - filterRunning: - description: |- - When true, the pods which are not running (e.g. either in Failed or - Succeeded state) are dropped during the target discovery. - - If unset, the filtering is enabled. - - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase - type: boolean - followRedirects: - description: |- - `followRedirects` defines whether the scrape requests should follow HTTP - 3xx redirects. - type: boolean - honorLabels: - description: |- - When true, `honorLabels` preserves the metric's labels when they collide - with the target's labels. - type: boolean - honorTimestamps: - description: |- - `honorTimestamps` controls whether Prometheus preserves the timestamps - when exposed by the target. - type: boolean - interval: - description: |- - Interval at which Prometheus scrapes the metrics from the target. - - If empty, Prometheus uses the global scrape interval. - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - metricRelabelings: - description: |- - `metricRelabelings` configures the relabeling rules to apply to the - samples before ingestion. - items: - description: |- - RelabelConfig allows dynamic rewriting of the label set for targets, alerts, - scraped samples and remote write samples. - - More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config - properties: - action: - default: replace - description: |- - Action to perform based on the regex matching. - - `Uppercase` and `Lowercase` actions require Prometheus >= v2.36.0. - `DropEqual` and `KeepEqual` actions require Prometheus >= v2.41.0. - - Default: "Replace" - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - description: |- - Modulus to take of the hash of the source label values. - - Only applicable when the action is `HashMod`. - format: int64 - type: integer - regex: - description: Regular expression against which the extracted - value is matched. - type: string - replacement: - description: |- - Replacement value against which a Replace action is performed if the - regular expression matches. - - Regex capture groups are available. - type: string - separator: - description: Separator is the string between concatenated - SourceLabels. - type: string - sourceLabels: - description: |- - The source labels select values from existing labels. Their content is - concatenated using the configured Separator and matched against the - configured regular expression. - items: - description: |- - LabelName is a valid Prometheus label name which may only contain ASCII - letters, numbers, as well as underscores. - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - description: |- - Label to which the resulting string is written in a replacement. - - It is mandatory for `Replace`, `HashMod`, `Lowercase`, `Uppercase`, - `KeepEqual` and `DropEqual` actions. - - Regex capture groups are available. - type: string - type: object - type: array - oauth2: - description: |- - `oauth2` configures the OAuth2 settings to use when scraping the target. - - It requires Prometheus >= 2.27.0. - - Cannot be set at the same time as `authorization`, or `basicAuth`. - properties: - clientId: - description: |- - `clientId` specifies a key of a Secret or ConfigMap containing the - OAuth2 client's ID. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: |- - `clientSecret` specifies a key of a Secret containing the OAuth2 - client's secret. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: |- - `endpointParams` configures the HTTP parameters to append to the token - URL. - type: object - noProxy: - description: |- - `noProxy` is a comma-separated string that can contain IPs, CIDR notation, domain names - that should be excluded from proxying. IP and domain names can - contain port numbers. - - It requires Prometheus >= v2.43.0 or Alertmanager >= 0.25.0. - type: string - proxyConnectHeader: - additionalProperties: - items: - description: SecretKeySelector selects a key of a Secret. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: array - description: |- - ProxyConnectHeader optionally specifies headers to send to - proxies during CONNECT requests. - - It requires Prometheus >= v2.43.0 or Alertmanager >= 0.25.0. - type: object - x-kubernetes-map-type: atomic - proxyFromEnvironment: - description: |- - Whether to use the proxy configuration defined by environment variables (HTTP_PROXY, HTTPS_PROXY, and NO_PROXY). - - It requires Prometheus >= v2.43.0 or Alertmanager >= 0.25.0. - type: boolean - proxyUrl: - description: '`proxyURL` defines the HTTP proxy server to - use.' - pattern: ^http(s)?://.+$ - type: string - scopes: - description: '`scopes` defines the OAuth2 scopes used for - the token request.' - items: - type: string - type: array - tlsConfig: - description: |- - TLS configuration to use when connecting to the OAuth2 server. - It requires Prometheus >= v2.43.0. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to use for - the targets. - properties: - key: - description: The key to select. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the ConfigMap or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the - targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when doing - client-authentication. - properties: - configMap: - description: ConfigMap containing data to use for - the targets. - properties: - key: - description: The key to select. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the ConfigMap or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the - targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key file for - the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - description: |- - Maximum acceptable TLS version. - - It requires Prometheus >= v2.41.0. - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - description: |- - Minimum acceptable TLS version. - - It requires Prometheus >= v2.35.0. - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - serverName: - description: Used to verify the hostname for the targets. - type: string - type: object - tokenUrl: - description: '`tokenURL` configures the URL to fetch the - token from.' - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - params: - additionalProperties: - items: - type: string - type: array - description: params define optional HTTP URL parameters. - type: object - path: - description: |- - HTTP path from which to scrape for metrics. - - If empty, Prometheus uses the default value (e.g. `/metrics`). - type: string - port: - description: |- - Name of the Service port which this endpoint refers to. - - It takes precedence over `targetPort`. - type: string - proxyUrl: - description: |- - `proxyURL` configures the HTTP Proxy URL (e.g. - "http://proxyserver:2195") to go through when scraping the target. - type: string - relabelings: - description: |- - `relabelings` configures the relabeling rules to apply the target's - metadata labels. - - The Operator automatically adds relabelings for a few standard Kubernetes fields. - - The original scrape job's name is available via the `__tmp_prometheus_job_name` label. - - More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config - items: - description: |- - RelabelConfig allows dynamic rewriting of the label set for targets, alerts, - scraped samples and remote write samples. - - More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config - properties: - action: - default: replace - description: |- - Action to perform based on the regex matching. - - `Uppercase` and `Lowercase` actions require Prometheus >= v2.36.0. - `DropEqual` and `KeepEqual` actions require Prometheus >= v2.41.0. - - Default: "Replace" - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - description: |- - Modulus to take of the hash of the source label values. - - Only applicable when the action is `HashMod`. - format: int64 - type: integer - regex: - description: Regular expression against which the extracted - value is matched. - type: string - replacement: - description: |- - Replacement value against which a Replace action is performed if the - regular expression matches. - - Regex capture groups are available. - type: string - separator: - description: Separator is the string between concatenated - SourceLabels. - type: string - sourceLabels: - description: |- - The source labels select values from existing labels. Their content is - concatenated using the configured Separator and matched against the - configured regular expression. - items: - description: |- - LabelName is a valid Prometheus label name which may only contain ASCII - letters, numbers, as well as underscores. - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - description: |- - Label to which the resulting string is written in a replacement. - - It is mandatory for `Replace`, `HashMod`, `Lowercase`, `Uppercase`, - `KeepEqual` and `DropEqual` actions. - - Regex capture groups are available. - type: string - type: object - type: array - scheme: - description: |- - HTTP scheme to use for scraping. - - `http` and `https` are the expected values unless you rewrite the - `__scheme__` label via relabeling. - - If empty, Prometheus uses the default value `http`. - enum: - - http - - https - type: string - scrapeTimeout: - description: |- - Timeout after which Prometheus considers the scrape to be failed. - - If empty, Prometheus uses the global scrape timeout unless it is less - than the target's scrape interval value in which the latter is used. - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - targetPort: - anyOf: - - type: integer - - type: string - description: |- - Name or number of the target port of the `Pod` object behind the - Service. The port must be specified with the container's port property. - x-kubernetes-int-or-string: true - tlsConfig: - description: TLS configuration to use when scraping the target. - properties: - ca: - description: Certificate authority used when verifying server - certificates. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - caFile: - description: Path to the CA cert in the Prometheus container - to use for the targets. - type: string - cert: - description: Client certificate to present when doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - certFile: - description: Path to the client cert file in the Prometheus - container for the targets. - type: string - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keyFile: - description: Path to the client key file in the Prometheus - container for the targets. - type: string - keySecret: - description: Secret containing the client key file for the - targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - description: |- - Maximum acceptable TLS version. - - It requires Prometheus >= v2.41.0. - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - minVersion: - description: |- - Minimum acceptable TLS version. - - It requires Prometheus >= v2.35.0. - enum: - - TLS10 - - TLS11 - - TLS12 - - TLS13 - type: string - serverName: - description: Used to verify the hostname for the targets. - type: string - type: object - trackTimestampsStaleness: - description: |- - `trackTimestampsStaleness` defines whether Prometheus tracks staleness of - the metrics that have an explicit timestamp present in scraped data. - Has no effect if `honorTimestamps` is false. - - It requires Prometheus >= v2.48.0. - type: boolean - type: object - type: array - jobLabel: - description: |- - `jobLabel` selects the label from the associated Kubernetes `Service` - object which will be used as the `job` label for all metrics. - - For example if `jobLabel` is set to `foo` and the Kubernetes `Service` - object is labeled with `foo: bar`, then Prometheus adds the `job="bar"` - label to all ingested metrics. - - If the value of this field is empty or if the label doesn't exist for - the given Service, the `job` label of the metrics defaults to the name - of the associated Kubernetes `Service`. - type: string - keepDroppedTargets: - description: |- - Per-scrape limit on the number of targets dropped by relabeling - that will be kept in memory. 0 means no limit. - - It requires Prometheus >= v2.47.0. - format: int64 - type: integer - labelLimit: - description: |- - Per-scrape limit on number of labels that will be accepted for a sample. - - It requires Prometheus >= v2.27.0. - format: int64 - type: integer - labelNameLengthLimit: - description: |- - Per-scrape limit on length of labels name that will be accepted for a sample. - - It requires Prometheus >= v2.27.0. - format: int64 - type: integer - labelValueLengthLimit: - description: |- - Per-scrape limit on length of labels value that will be accepted for a sample. - - It requires Prometheus >= v2.27.0. - format: int64 - type: integer - namespaceSelector: - description: |- - `namespaceSelector` defines in which namespace(s) Prometheus should discover the services. - By default, the services are discovered in the same namespace as the `ServiceMonitor` object but it is possible to select pods across different/all namespaces. - properties: - any: - description: |- - Boolean describing whether all namespaces are selected in contrast to a - list restricting them. - type: boolean - matchNames: - description: List of namespace names to select from. - items: - type: string - type: array - type: object - nativeHistogramBucketLimit: - description: |- - If there are more than this many buckets in a native histogram, - buckets will be merged to stay within the limit. - It requires Prometheus >= v2.45.0. - format: int64 - type: integer - nativeHistogramMinBucketFactor: - anyOf: - - type: integer - - type: string - description: |- - If the growth factor of one bucket to the next is smaller than this, - buckets will be merged to increase the factor sufficiently. - It requires Prometheus >= v2.50.0. - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - podTargetLabels: - description: |- - `podTargetLabels` defines the labels which are transferred from the - associated Kubernetes `Pod` object onto the ingested metrics. - items: - type: string - type: array - sampleLimit: - description: |- - `sampleLimit` defines a per-scrape limit on the number of scraped samples - that will be accepted. - format: int64 - type: integer - scrapeClass: - description: The scrape class to apply. - minLength: 1 - type: string - scrapeClassicHistograms: - description: |- - Whether to scrape a classic histogram that is also exposed as a native histogram. - It requires Prometheus >= v2.45.0. - type: boolean - scrapeFallbackProtocol: - description: |- - The protocol to use if a scrape returns blank, unparseable, or otherwise invalid Content-Type. - - It requires Prometheus >= v3.0.0. - enum: - - PrometheusProto - - OpenMetricsText0.0.1 - - OpenMetricsText1.0.0 - - PrometheusText0.0.4 - - PrometheusText1.0.0 - type: string - scrapeProtocols: - description: |- - `scrapeProtocols` defines the protocols to negotiate during a scrape. It tells clients the - protocols supported by Prometheus in order of preference (from most to least preferred). - - If unset, Prometheus uses its default value. - - It requires Prometheus >= v2.49.0. - items: - description: |- - ScrapeProtocol represents a protocol used by Prometheus for scraping metrics. - Supported values are: - * `OpenMetricsText0.0.1` - * `OpenMetricsText1.0.0` - * `PrometheusProto` - * `PrometheusText0.0.4` - * `PrometheusText1.0.0` - enum: - - PrometheusProto - - OpenMetricsText0.0.1 - - OpenMetricsText1.0.0 - - PrometheusText0.0.4 - - PrometheusText1.0.0 - type: string - type: array - x-kubernetes-list-type: set - selector: - description: Label selector to select the Kubernetes `Endpoints` objects - to scrape metrics from. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - targetLabels: - description: |- - `targetLabels` defines the labels which are transferred from the - associated Kubernetes `Service` object onto the ingested metrics. - items: - type: string - type: array - targetLimit: - description: |- - `targetLimit` defines a limit on the number of scraped targets that will - be accepted. - format: int64 - type: integer - required: - - endpoints - - selector - type: object - required: - - spec - type: object - served: true - storage: true diff --git a/hack/tools/kapinger/clients/http.go b/hack/tools/kapinger/clients/http.go index 027e073c45..471fe2f00e 100644 --- a/hack/tools/kapinger/clients/http.go +++ b/hack/tools/kapinger/clients/http.go @@ -21,11 +21,7 @@ const ( Service TargetType = "service" Pod TargetType = "pod" - envTargetType = "TARGET_TYPE" - defaultHTTPClientTimeout = 30 * time.Second - - defaultRetryAttempts = 10 - defaultRetryDelay = 5 * time.Second + envTargetType = "TARGET_TYPE" ) type KapingerHTTPClient struct { @@ -45,7 +41,7 @@ func NewKapingerHTTPClient(clientset *kubernetes.Clientset, labelselector string Transport: &http.Transport{ DisableKeepAlives: true, }, - Timeout: defaultHTTPClientTimeout, + Timeout: 3 * time.Second, }, labelselector: labelselector, clientset: clientset, @@ -76,7 +72,7 @@ func NewKapingerHTTPClient(clientset *kubernetes.Clientset, labelselector string return nil, fmt.Errorf("env TARGET_TYPE must be \"service\" or \"pod\"") } if err != nil { - return nil, err + return nil, fmt.Errorf("error getting IPs: %w", err) } return &k, nil @@ -106,30 +102,10 @@ func (k *KapingerHTTPClient) MakeRequests(ctx context.Context) error { } } -func (k *KapingerHTTPClient) makeRequest() error { - for _, ip := range k.ips { - url := fmt.Sprintf("http://%s:%d", ip, k.port) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return err - } - - // Set the "Connection" header to "close" - req.Header.Set("Connection", "close") - - // Send the request - resp, err := k.client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - log.Fatalf("http client: error reading response body from %s: %v", url, err) - return err - } - log.Printf("http client: response from %s: %s", url, string(body)) +func (k *KapingerHTTPClient) makeRequest(ctx context.Context, url string) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, "GET", url, http.NoBody) + if err != nil { + return nil, err } // Set the "Connection" header to "close" @@ -151,46 +127,13 @@ func (k *KapingerHTTPClient) makeRequest() error { return body, nil } -func (k *KapingerHTTPClient) getIPS() error { - ips := []string{} - - switch k.targettype { - case Service: - services, err := k.clientset.CoreV1().Services(corev1.NamespaceAll).List(context.Background(), metav1.ListOptions{ - LabelSelector: k.labelselector, - }) - if err != nil { - return fmt.Errorf("http client: error getting services: %w", err) - } - - // Extract the Service cluster IP addresses - - for _, svc := range services.Items { - ips = append(ips, svc.Spec.ClusterIP) - } - log.Println("http client: using service IPs:", ips) - - case Pod: - err := waitForPodsRunning(k.clientset, k.labelselector) - if err != nil { - return fmt.Errorf("http client: error waiting for pods to be in Running state: %w", err) - } - - // Get all pods in the cluster with label app=agnhost - pods, err := k.clientset.CoreV1().Pods(corev1.NamespaceAll).List(context.Background(), metav1.ListOptions{ - LabelSelector: k.labelselector, - }) - if err != nil { - return fmt.Errorf("http client: error getting pods: %w", err) - } - - for _, pod := range pods.Items { - ips = append(ips, pod.Status.PodIP) - } - - log.Printf("using pod IPs: %v", ips) - default: - return fmt.Errorf("env TARGET_TYPE must be \"service\" or \"pod\"") +func (k *KapingerHTTPClient) getServiceURLs() ([]string, error) { + urls := []string{} + services, err := k.clientset.CoreV1().Services(corev1.NamespaceAll).List(context.Background(), metav1.ListOptions{ + LabelSelector: k.labelselector, + }) + if err != nil { + return urls, fmt.Errorf("error getting services: %w", err) } // Extract the Service cluster IP addresses diff --git a/test/e2e/framework/kubernetes/check-pod-status.go b/test/e2e/framework/kubernetes/check-pod-status.go index d2cfe0408b..c360400b81 100644 --- a/test/e2e/framework/kubernetes/check-pod-status.go +++ b/test/e2e/framework/kubernetes/check-pod-status.go @@ -59,6 +59,8 @@ func (w *WaitPodsReady) Stop() error { } func WaitForPodReady(ctx context.Context, clientset *kubernetes.Clientset, namespace, labelSelector string) error { + podReadyMap := make(map[string]bool) + printIterator := 0 conditionFunc := wait.ConditionWithContextFunc(func(context.Context) (bool, error) { defer func() { @@ -77,12 +79,10 @@ func WaitForPodReady(ctx context.Context, clientset *kubernetes.Clientset, names // check each indviidual pod to see if it's in Running state for i := range podList.Items { - pod := &podList.Items[i] - for istatus := range pod.Status.ContainerStatuses { - status := &pod.Status.ContainerStatuses[istatus] - if status.RestartCount > 0 { - return false, fmt.Errorf("pod %s has %d restarts: status: %+v: %w", pod.Name, status.RestartCount, status, ErrPodCrashed) - } + var pod *corev1.Pod + pod, err = clientset.CoreV1().Pods(namespace).Get(ctx, podList.Items[i].Name, metav1.GetOptions{}) + if err != nil { + return false, fmt.Errorf("error getting Pod: %w", err) } // Check the Pod phase @@ -93,6 +93,19 @@ func WaitForPodReady(ctx context.Context, clientset *kubernetes.Clientset, names return false, nil } + // Check all container status. + for i := range pod.Status.ContainerStatuses { + containerStatus := &pod.Status.ContainerStatuses[i] + if !containerStatus.Ready { + log.Printf("container \"%s\" in pod \"%s\" is not ready yet. Waiting...\n", containerStatus.Name, pod.Name) + return false, nil + } + } + + if !podReadyMap[pod.Name] { + log.Printf("pod \"%s\" is in Running state\n", pod.Name) + podReadyMap[pod.Name] = true + } } log.Printf("all pods in namespace \"%s\" with label \"%s\" are in Running state\n", namespace, labelSelector) return true, nil diff --git a/test/e2e/framework/kubernetes/validateHttp.go b/test/e2e/framework/kubernetes/validateHttp.go index 8400647714..45fa3624f7 100644 --- a/test/e2e/framework/kubernetes/validateHttp.go +++ b/test/e2e/framework/kubernetes/validateHttp.go @@ -34,7 +34,7 @@ func (v *ValidateHTTPResponse) Run() error { defer resp.Body.Close() if resp.StatusCode != v.ExpectedStatus { - return fmt.Errorf("unexpected status code: got %d, want %d", resp.StatusCode, v.ExpectedStatus) + return fmt.Errorf("unexpected status code: got %d, want %d", resp.StatusCode, v.ExpectedStatus) //nolint:goerr113 no formatting needed } log.Printf("HTTP validation succeeded for URL: %s with status code %d\n", v.URL, resp.StatusCode) diff --git a/test/e2e/framework/scaletest/add-shared-labels.go b/test/e2e/framework/scaletest/add-shared-labels.go index d76139c0be..086f196696 100644 --- a/test/e2e/framework/scaletest/add-shared-labels.go +++ b/test/e2e/framework/scaletest/add-shared-labels.go @@ -35,7 +35,6 @@ func (a *AddSharedLabelsToAllPods) Prevalidate() error { // Primary step where test logic is executed // Returning an error will cause the test to fail func (a *AddSharedLabelsToAllPods) Run() error { - if a.NumSharedLabelsPerPod < 1 { return nil } diff --git a/test/e2e/framework/scaletest/add-unique-labels.go b/test/e2e/framework/scaletest/add-unique-labels.go index cfdd458c82..c4d9a3eff7 100644 --- a/test/e2e/framework/scaletest/add-unique-labels.go +++ b/test/e2e/framework/scaletest/add-unique-labels.go @@ -29,7 +29,6 @@ func (a *AddUniqueLabelsToAllPods) Prevalidate() error { // Primary step where test logic is executed // Returning an error will cause the test to fail func (a *AddUniqueLabelsToAllPods) Run() error { - if a.NumUniqueLabelsPerPod < 1 { return nil } diff --git a/test/e2e/framework/scaletest/create-network-policies.go b/test/e2e/framework/scaletest/create-network-policies.go index c38f9597cc..ebf0f08e83 100644 --- a/test/e2e/framework/scaletest/create-network-policies.go +++ b/test/e2e/framework/scaletest/create-network-policies.go @@ -34,7 +34,6 @@ func (c *CreateNetworkPolicies) Prevalidate() error { // Primary step where test logic is executed // Returning an error will cause the test to fail func (c *CreateNetworkPolicies) Run() error { - config, err := clientcmd.BuildConfigFromFlags("", c.KubeConfigFilePath) if err != nil { return fmt.Errorf("error building kubeconfig: %w", err) diff --git a/test/e2e/framework/scaletest/delete-and-re-add-labels.go b/test/e2e/framework/scaletest/delete-and-re-add-labels.go index 5897b4d766..68a78b1cad 100644 --- a/test/e2e/framework/scaletest/delete-and-re-add-labels.go +++ b/test/e2e/framework/scaletest/delete-and-re-add-labels.go @@ -33,7 +33,6 @@ func (d *DeleteAndReAddLabels) Prevalidate() error { // Primary step where test logic is executed // Returning an error will cause the test to fail func (d *DeleteAndReAddLabels) Run() error { - if d.NumSharedLabelsPerPod <= 2 || !d.DeleteLabels { return nil } @@ -89,7 +88,6 @@ func (d *DeleteAndReAddLabels) Run() error { } func (d *DeleteAndReAddLabels) addLabels(ctx context.Context, clientset *kubernetes.Clientset, pods *corev1.PodList, patch string) error { - for _, pod := range pods.Items { _, err := clientset.CoreV1().Pods(d.Namespace).Patch(ctx, pod.Name, types.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{}) if err != nil { @@ -101,7 +99,6 @@ func (d *DeleteAndReAddLabels) addLabels(ctx context.Context, clientset *kuberne } func (d *DeleteAndReAddLabels) deleteLabels(ctx context.Context, clientset *kubernetes.Clientset, pods *corev1.PodList, patch string) error { - for _, pod := range pods.Items { _, err := clientset.CoreV1().Pods(d.Namespace).Patch(ctx, pod.Name, types.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{}) if err != nil { diff --git a/test/e2e/framework/scaletest/get-publish-metrics.go b/test/e2e/framework/scaletest/get-publish-metrics.go index 3495addf33..c576dc2959 100644 --- a/test/e2e/framework/scaletest/get-publish-metrics.go +++ b/test/e2e/framework/scaletest/get-publish-metrics.go @@ -47,7 +47,6 @@ func (g *GetAndPublishMetrics) Run() error { g.wg.Add(1) go func() { - t := time.NewTicker(5 * time.Minute) for { @@ -66,7 +65,6 @@ func (g *GetAndPublishMetrics) Run() error { } } - }() return nil @@ -92,7 +90,6 @@ func (g *GetAndPublishMetrics) Prevalidate() error { } func (g *GetAndPublishMetrics) getAndPublishMetrics() error { - config, err := clientcmd.BuildConfigFromFlags("", g.KubeConfigFilePath) if err != nil { return fmt.Errorf("error building kubeconfig: %w", err) @@ -121,14 +118,13 @@ func (g *GetAndPublishMetrics) getAndPublishMetrics() error { log.Println("Publishing metrics to AppInsights") for _, metric := range metrics { g.telemetryClient.TrackEvent("scale-test", metric) - } } // Write metrics to file if g.OutputFilePath != "" { log.Println("Writing metrics to file ", g.OutputFilePath) - file, err := os.OpenFile(g.OutputFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + file, err := os.OpenFile(g.OutputFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { return fmt.Errorf("error writing to csv file: %w", err) } @@ -151,7 +147,6 @@ func (g *GetAndPublishMetrics) getAndPublishMetrics() error { type metric map[string]string func (g *GetAndPublishMetrics) getMetrics(ctx context.Context, k8sClient *kubernetes.Clientset, metricsClient *metrics.Clientset) ([]metric, error) { - labelSelector := labels.Set(g.Labels).String() pods, err := k8sClient.CoreV1().Pods(common.KubeSystemNamespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector}) diff --git a/test/e2e/framework/scaletest/templates/networkpolicy.go b/test/e2e/framework/scaletest/templates/networkpolicy.go index 2eef3e3161..936767b3d7 100644 --- a/test/e2e/framework/scaletest/templates/networkpolicy.go +++ b/test/e2e/framework/scaletest/templates/networkpolicy.go @@ -5,23 +5,21 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var ( - NetworkPolicy = netv1.NetworkPolicy{ - TypeMeta: metav1.TypeMeta{ - Kind: "NetworkPolicy", - APIVersion: "networking.k8s.io/v1", +var NetworkPolicy = netv1.NetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "template-network-policy", + }, + Spec: netv1.NetworkPolicySpec{ + PolicyTypes: []netv1.PolicyType{ + "Ingress", + "Egress", }, - ObjectMeta: metav1.ObjectMeta{ - Name: "template-network-policy", + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{}, }, - Spec: netv1.NetworkPolicySpec{ - PolicyTypes: []netv1.PolicyType{ - "Ingress", - "Egress", - }, - PodSelector: metav1.LabelSelector{ - MatchLabels: map[string]string{}, - }, - }, - } -) + }, +} diff --git a/test/e2e/framework/scaletest/validate-options.go b/test/e2e/framework/scaletest/validate-options.go index 0dafdd2b06..a091a6f61f 100644 --- a/test/e2e/framework/scaletest/validate-options.go +++ b/test/e2e/framework/scaletest/validate-options.go @@ -37,8 +37,7 @@ func (po *ValidateAndPrintOptions) Prevalidate() error { // Returning an error will cause the test to fail func (po *ValidateAndPrintOptions) Run() error { - - log.Printf("Starting to scale with folowing options: %+v", po.Options) + log.Printf("Starting to scale with following options: %+v", po.Options) return nil } diff --git a/test/e2e/jobs/jobs.go b/test/e2e/jobs/jobs.go index ddc58b5daf..0f1a1978b9 100644 --- a/test/e2e/jobs/jobs.go +++ b/test/e2e/jobs/jobs.go @@ -365,4 +365,3 @@ func RunPerfTest(kubeConfigFilePath string, chartPath string) *types.Job { return job } - diff --git a/test/e2e/jobs/scale.go b/test/e2e/jobs/scale.go index 89215785c1..1be75c8e18 100644 --- a/test/e2e/jobs/scale.go +++ b/test/e2e/jobs/scale.go @@ -66,7 +66,7 @@ func ScaleTest(opt *scaletest.Options) *types.Job { job.AddStep(&scaletest.GetAndPublishMetrics{ Labels: opt.LabelsToGetMetrics, AdditionalTelemetryProperty: opt.AdditionalTelemetryProperty, - OutputFilePath: os.Getenv("OUTPUT_FILEPATH"), + OutputFilePath: os.Getenv("OUTPUT_FILEPATH"), }, &types.StepOptions{ SkipSavingParametersToJob: true, RunInBackgroundWithID: "get-metrics", diff --git a/test/e2e/retina_longrunning_test.go b/test/e2e/retina_longrunning_test.go index 60483490c2..df51c04e5a 100644 --- a/test/e2e/retina_longrunning_test.go +++ b/test/e2e/retina_longrunning_test.go @@ -3,6 +3,7 @@ package retina import ( "testing" + "github.com/microsoft/retina/test/e2e/framework/helpers" "github.com/microsoft/retina/test/e2e/framework/types" jobs "github.com/microsoft/retina/test/e2e/jobs" "github.com/stretchr/testify/require" @@ -10,18 +11,21 @@ import ( // Scrape PProf over a long running datapath tests func TestLongRunningRetina(t *testing.T) { + ctx, cancel := helpers.Context(t) + defer cancel() + settings, err := LoadInfraSettings() require.NoError(t, err) // CreateTestInfra - createTestInfra := types.NewRunner(t, jobs.CreateTestInfra(subID, clusterName, location, settings.KubeConfigFilePath, settings.CreateInfra)) - createTestInfra.Run() + createTestInfra := types.NewRunner(t, jobs.CreateTestInfra(subID, resourceGroup, clusterName, location, settings.KubeConfigFilePath, settings.CreateInfra)) + createTestInfra.Run(ctx) t.Cleanup(func() { if settings.DeleteInfra { - _ = jobs.DeleteTestInfra(subID, clusterName, location).Run() + _ = jobs.DeleteTestInfra(subID, resourceGroup, clusterName, location).Run() } }) longrunning := types.NewRunner(t, jobs.CreateLongRunningTest(subID, clusterName, location, settings.KubeConfigFilePath, settings.CreateInfra)) - longrunning.Run() + longrunning.Run(ctx) } diff --git a/test/e2e/scenarios/perf/publish-perf-results.go b/test/e2e/scenarios/perf/publish-perf-results.go index 4f1dd05344..aeb760fb6b 100644 --- a/test/e2e/scenarios/perf/publish-perf-results.go +++ b/test/e2e/scenarios/perf/publish-perf-results.go @@ -28,7 +28,7 @@ func (v *PublishPerfResults) Run() error { return nil } - resultsFile, err := os.OpenFile(v.ResultsFile, os.O_RDONLY, 0644) + resultsFile, err := os.OpenFile(v.ResultsFile, os.O_RDONLY, 0o644) if err != nil { return errors.Wrap(err, "failed to open results file") } diff --git a/test/e2e/settings_test.go b/test/e2e/settings_test.go index 1ed4570b43..bd563f913f 100644 --- a/test/e2e/settings_test.go +++ b/test/e2e/settings_test.go @@ -2,6 +2,7 @@ package retina import ( "crypto/rand" + "errors" "flag" "fmt" "log" @@ -16,9 +17,12 @@ import ( ) var ( - clusterName = os.Getenv("CLUSTER_NAME") - subID = os.Getenv("AZURE_SUBSCRIPTION_ID") - location = os.Getenv("AZURE_LOCATION") + clusterName = os.Getenv("CLUSTER_NAME") + subID = os.Getenv("AZURE_SUBSCRIPTION_ID") + location = os.Getenv("AZURE_LOCATION") + resourceGroup = os.Getenv("AZURE_RESOURCE_GROUP") + + errAzureSubscriptionIDNotSet = errors.New("AZURE_SUBSCRIPTION_ID is not set") ) var ( @@ -38,7 +42,7 @@ type TestInfraSettings struct { func LoadInfraSettings() (*TestInfraSettings, error) { curuser, err := user.Current() if err != nil { - return nil, err + return nil, fmt.Errorf("Failed to get current user: %w", err) } flag.Parse() @@ -49,21 +53,26 @@ func LoadInfraSettings() (*TestInfraSettings, error) { } if subID == "" { - return nil, fmt.Errorf("AZURE_SUBSCRIPTION_ID is not set") + return nil, errAzureSubscriptionIDNotSet } if location == "" { var nBig *big.Int nBig, err = rand.Int(rand.Reader, big.NewInt(int64(len(locations)))) if err != nil { - return nil, fmt.Errorf("Failed to generate a secure random index: %v", err) + return nil, fmt.Errorf("Failed to generate a secure random index: %w", err) } location = locations[nBig.Int64()] } + if resourceGroup == "" { + log.Printf("AZURE_RESOURCE_GROUP is not set, using the cluster name as the resource group name by default") + resourceGroup = clusterName + } + cwd, err := os.Getwd() if err != nil { - return nil, err + return nil, fmt.Errorf("Failed to get current working directory: %w", err) } // Get to root of the repo by going up two directories