Skip to content

Commit

Permalink
Replace shell call of curl with net/http implementation in Prometheus…
Browse files Browse the repository at this point in the history
… helper functions.
  • Loading branch information
raptorsun committed Nov 8, 2023
1 parent ba9c25c commit 3b61545
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 45 deletions.
39 changes: 9 additions & 30 deletions test/extended/prometheus/prometheus.go
Expand Up @@ -5,9 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -367,7 +365,7 @@ var _ = g.Describe("[sig-instrumentation] Prometheus [apigroup:image.openshift.i

g.By("checking the prometheus metrics path")
var metrics map[string]*dto.MetricFamily
o.Expect(wait.PollImmediate(10*time.Second, 2*time.Minute, func() (bool, error) {
o.Expect(wait.PollUntilContextTimeout(context.Background(), 10*time.Second, 2*time.Minute, true, func(context.Context) (bool, error) {
results, err := getBearerTokenURLViaPod(ns, execPod.Name, fmt.Sprintf("%s/metrics", prometheusSvcURL), bearerToken)
if err != nil {
e2e.Logf("unable to get metrics: %v", err)
Expand Down Expand Up @@ -400,14 +398,16 @@ var _ = g.Describe("[sig-instrumentation] Prometheus [apigroup:image.openshift.i
o.Expect(err).NotTo(o.HaveOccurred())

g.By("verifying a service account token is able to authenticate")
err = expectBearerTokenURLStatusCodeExec(fmt.Sprintf("%s/api/v1/targets", queryURL), bearerToken, 200)

err = helper.ExpectURLStatusCodeExec(helper.MustJoinUrlPath(queryURL, "api/v1/targets"), bearerToken, 200)

o.Expect(err).NotTo(o.HaveOccurred())

g.By("verifying a service account token is able to access the Prometheus API")
// expect all endpoints within 60 seconds
var lastErrs []error
o.Expect(wait.PollImmediate(10*time.Second, 2*time.Minute, func() (bool, error) {
contents, err := getBearerTokenURL(fmt.Sprintf("%s/api/v1/targets", prometheusURL), bearerToken)
o.Expect(wait.PollUntilContextTimeout(context.Background(), 10*time.Second, 2*time.Minute, true, func(context.Context) (bool, error) {
contents, err := helper.GetBearerTokenURL(helper.MustJoinUrlPath(prometheusURL, "api/v1/targets"), bearerToken)
o.Expect(err).NotTo(o.HaveOccurred())

targets := &prometheusTargets{}
Expand Down Expand Up @@ -456,7 +456,7 @@ var _ = g.Describe("[sig-instrumentation] Prometheus [apigroup:image.openshift.i

g.By("verifying all targets are exposing metrics over secure channel")
var insecureTargets []error
contents, err := getBearerTokenURL(fmt.Sprintf("%s/api/v1/targets", prometheusURL), bearerToken)
contents, err := helper.GetBearerTokenURL(helper.MustJoinUrlPath(prometheusURL, "api/v1/targets"), bearerToken)
o.Expect(err).NotTo(o.HaveOccurred())

targets := &prometheusTargets{}
Expand Down Expand Up @@ -579,8 +579,8 @@ var _ = g.Describe("[sig-instrumentation] Prometheus [apigroup:image.openshift.i

g.It("should provide ingress metrics", func() {
var lastErrs []error
o.Expect(wait.PollImmediate(10*time.Second, 4*time.Minute, func() (bool, error) {
contents, err := getBearerTokenURL(fmt.Sprintf("%s/api/v1/targets", prometheusURL), bearerToken)
o.Expect(wait.PollUntilContextTimeout(context.Background(), 10*time.Second, 4*time.Minute, true, func(ctx context.Context) (bool, error) {
contents, err := helper.GetBearerTokenURL(helper.MustJoinUrlPath(prometheusURL, "api/v1/targets"), bearerToken)
o.Expect(err).NotTo(o.HaveOccurred())

targets := &prometheusTargets{}
Expand Down Expand Up @@ -758,27 +758,6 @@ func findMetricLabels(f *dto.MetricFamily, labels map[string]string, match strin
return result
}

func expectBearerTokenURLStatusCodeExec(url, bearer string, statusCode int) error {
cmd := fmt.Sprintf("curl -k -s -H 'Authorization: Bearer %s' -o /dev/null -w '%%{http_code}' %q", bearer, url)
output, err := exec.Command("bash", "-e", "-c", cmd).Output()
if err != nil {
return fmt.Errorf("host command failed: %v\n%s", err, output)
}
if string(output) != strconv.Itoa(statusCode) {
return fmt.Errorf("last response from server was not %d: %s", statusCode, output)
}
return nil
}

func getBearerTokenURL(url, bearer string) (string, error) {
cmd := fmt.Sprintf("curl -s -k -H 'Authorization: Bearer %s' %q", bearer, url)
output, err := exec.Command("bash", "-e", "-c", cmd).Output()
if err != nil {
return "", fmt.Errorf("host command failed: %v\n%s", err, output)
}
return string(output), nil
}

func getBearerTokenURLViaPod(ns, execPodName, url, bearer string) (string, error) {
cmd := fmt.Sprintf("curl -s -k -H 'Authorization: Bearer %s' %q", bearer, url)
output, err := e2eoutput.RunHostCmd(ns, execPodName, cmd)
Expand Down
93 changes: 78 additions & 15 deletions test/extended/util/prometheus/helpers.go
Expand Up @@ -2,11 +2,12 @@ package prometheus

import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os/exec"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -45,13 +46,51 @@ type prometheusResponseData struct {
}

// GetBearerTokenURL makes http request with bearer token
func GetBearerTokenURL(url, bearer string) (string, error) {
cmd := fmt.Sprintf("curl --retry 15 --max-time 2 --retry-delay 1 -s -k -H 'Authorization: Bearer %s' %q", bearer, url)
output, err := exec.Command("bash", "-e", "-c", cmd).Output()
func GetBearerTokenURL(url, bearerToken string) (string, error) {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{
Timeout: time.Duration(10 * time.Second),
Transport: tr,
}

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", err
}

req.Header.Add("Authorization", "Bearer "+bearerToken)

var (
body []byte
lastErr error
)
condition := func(ctx context.Context) (bool, error) {
resp, err := client.Do(req)
if err != nil {
return false, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
lastErr = fmt.Errorf("%s: unexpected status code: %d", url, resp.StatusCode)
return false, err
}

body, err = io.ReadAll(resp.Body)
if err != nil {
lastErr = fmt.Errorf("%s: failed to read response: %w", url, err)
return false, nil
}

return true, nil
}
err = wait.PollUntilContextTimeout(context.Background(), 10*time.Second, 60*time.Second, true, condition)
if err != nil {
return "", fmt.Errorf("host command failed: %v\n%s", err, output)
return "", fmt.Errorf("%w: %w", err, lastErr)
}
return string(output), nil

return string(body), nil
}

func waitForServiceAccountInNamespace(c clientset.Interface, ns, serviceAccountName string, timeout time.Duration) error {
Expand Down Expand Up @@ -285,19 +324,34 @@ func RunQueries(ctx context.Context, prometheusClient prometheusv1.API, promQuer

// ExpectURLStatusCodeExec attempts connection to url returning an error
// upon failure or if status return code is not equal to any of the statusCodes.
func ExpectURLStatusCodeExec(url string, statusCodes ...int) error {
cmd := fmt.Sprintf("curl -k -s -o /dev/null -w '%%{http_code}' %q", url)
output, err := exec.Command("bash", "-e", "-c", cmd).Output()
func ExpectURLStatusCodeExec(url, bearerToken string, statusCodes ...int) error {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{
Transport: tr,
Timeout: time.Duration(10 * time.Second),
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("host command failed: %v\n%s", err, output)
return err
}
if len(bearerToken) > 0 {
req.Header.Add("Authorization", "Bearer "+bearerToken)
}

resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

for _, statusCode := range statusCodes {
if string(output) == strconv.Itoa(statusCode) {
if resp.StatusCode == statusCode {
return nil
}
}

return fmt.Errorf("last response from server was not in %v: %s", statusCodes, output)
return fmt.Errorf("%s: last response from server was not in %v: %d", url, statusCodes, resp.StatusCode)
}

// ExpectURLStatusCodeExecViaPod attempts connection to url via exec pod and returns an error
Expand All @@ -314,15 +368,15 @@ func ExpectURLStatusCodeExecViaPod(ns, execPodName, url string, statusCodes ...i
}
}

return fmt.Errorf("last response from server was not in %v: %s", statusCodes, output)
return fmt.Errorf("%s: last response from server was not in %v: %s", url, statusCodes, output)
}

// ExpectPrometheusEndpoint attempts to connect to the metrics endpoint with
// delayed retries upon failure.
func ExpectPrometheusEndpoint(url string) {
var err error
for i := 0; i < maxPrometheusQueryAttempts; i++ {
err = ExpectURLStatusCodeExec(url, 401, 403)
err = ExpectURLStatusCodeExec(url, "", 401, 403)
if err == nil {
break
}
Expand Down Expand Up @@ -444,3 +498,12 @@ func ForEachAlertingRule(rules map[string][]promv1.AlertingRule, f func(a promv1

return fmt.Errorf("Incompliant rules detected:\n\n%s", strings.Join(allViolations.List(), "\n"))
}

// MustJoinUrlPath behaves like url.JoinPath but it will panic in case of error.
func MustJoinUrlPath(base string, paths ...string) string {
path, err := url.JoinPath(base, paths...)
if err != nil {
panic(err)
}
return path
}

0 comments on commit 3b61545

Please sign in to comment.