Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

httptransport: notification content type, etc #1599

Merged
merged 3 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions httptransport/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ import (
"github.com/quay/claircore"
)

const (
metricNamespace = `clair`
metricSubsystem = `http`
)

// GetDigest removes the last path element and parses it as a digest.
func getDigest(_ http.ResponseWriter, r *http.Request) (d claircore.Digest, err error) {
dStr := path.Base(r.URL.Path)
Expand Down
34 changes: 14 additions & 20 deletions httptransport/discoveryhandler.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,42 @@
package httptransport

import (
"bytes"
_ "embed" // for json and etag
"errors"
"io"
"net/http"
"strings"

je "github.com/quay/claircore/pkg/jsonerr"
)

//go:generate go run openapigen.go

var (
//go:embed openapi.json
openapiJSON []byte
//go:embed openapi.etag
openapiJSONEtag string
)

// DiscoveryHandler serves the embedded OpenAPI spec.
func DiscoveryHandler() http.Handler {
allow := []string{`application/json`, `application/vnd.oai.openapi+json`}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
resp := &je.Response{
Code: "method-not-allowed",
Message: "endpoint only allows GET",
}
je.Error(w, resp, http.StatusMethodNotAllowed)
apiError(w, http.StatusMethodNotAllowed, "endpoint only allows GET")
return
}
switch err := pickContentType(w, r, allow); {
case errors.Is(err, nil):
case errors.Is(err, ErrMediaType):
resp := &je.Response{
Code: "unknown accept type",
Message: "endpoint only allows " + strings.Join(allow, " or "),
}
je.Error(w, resp, http.StatusUnsupportedMediaType)
apiError(w, http.StatusUnsupportedMediaType, "unable to negotiate common media type for %v", allow)
return
default:
resp := &je.Response{
Code: "unknown other error",
Message: err.Error(),
}
je.Error(w, resp, http.StatusBadRequest)
apiError(w, http.StatusInternalServerError, "unexpected error: %v", err)
return
}
w.Header().Set("etag", _openapiJSONEtag)
w.Header().Set("etag", openapiJSONEtag)
var err error
defer writerError(w, &err)()
_, err = io.WriteString(w, _openapiJSON)
_, err = io.Copy(w, bytes.NewReader(openapiJSON))
})
}
8 changes: 0 additions & 8 deletions httptransport/discoveryhandler_gen.go

This file was deleted.

44 changes: 25 additions & 19 deletions httptransport/discoveryhandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package httptransport

import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"

Expand Down Expand Up @@ -64,26 +64,32 @@ func TestDiscoveryFailure(t *testing.T) {
}

func TestEmbedding(t *testing.T) {
ctx, done := context.WithCancel(context.Background())
defer done()

var gend, written bytes.Buffer
cmd := exec.CommandContext(ctx, "go", "run", "openapigen.go", "-out", "/dev/stdout")
cmd.Stdout = &gend
d := t.TempDir()
var buf bytes.Buffer
cmd := exec.Command("go", "run", "openapigen.go", "-in", "../openapi.yaml", "-out", d)
cmd.Stdout = &buf
cmd.Stderr = &buf
t.Log(cmd.Args)
if err := cmd.Run(); err != nil {
t.Fatal(err)
}
f, err := os.Open("discoveryhandler_gen.go")
if err != nil {
t.Fatal(err)
}
defer f.Close()
if _, err := written.ReadFrom(f); err != nil {
t.Fatal(err)
t.Error(err)
t.Error(buf.String())
}

if got, want := gend.String(), written.String(); !cmp.Equal(got, want) {
t.Error(cmp.Diff(got, want, cmpopts.AcyclicTransformer("normalizeWhitespace", func(s string) []string { return strings.Split(s, "\n") })))
t.Log("\n\tYou probably edited the openapi.yaml and forgot to run `go generate` here.")
for _, n := range []string{
"openapi.json", "openapi.etag"} {
new, err := os.ReadFile(filepath.Join(d, n))
if err != nil {
t.Error(err)
continue
}
old, err := os.ReadFile(n)
if err != nil {
t.Error(err)
continue
}
if got, want := string(new), string(old); !cmp.Equal(got, want) {
t.Error(cmp.Diff(got, want, cmpopts.AcyclicTransformer("normalizeWhitespace", func(s string) []string { return strings.Split(s, "\n") })))
t.Log("\n\tYou probably edited the openapi.yaml and forgot to run `go generate` here.")
}
}
}
51 changes: 2 additions & 49 deletions httptransport/indexer_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"time"

"github.com/ldelossa/responserecorder"
"github.com/prometheus/client_golang/prometheus"
"github.com/quay/claircore"
"github.com/quay/zlog"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
Expand Down Expand Up @@ -294,53 +293,7 @@ func (h *IndexerV1) affectedManifests(w http.ResponseWriter, r *http.Request) {
}

func init() {
indexerv1wrapper.init()
indexerv1wrapper.init("indexerv1")
}

var indexerv1wrapper = &wrapper{
RequestCount: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "indexerv1_request_total",
Help: "A total count of http requests for the given path",
},
[]string{"handler", "code", "method"},
),
RequestSize: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "indexerv1_request_size_bytes",
Help: "Distribution of request sizes for the given path",
},
[]string{"handler", "code", "method"},
),
ResponseSize: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "indexerv1_response_size_bytes",
Help: "Distribution of response sizes for the given path",
}, []string{"handler", "code", "method"},
),
RequestDuration: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "indexerv1_request_duration_seconds",
Help: "Distribution of request durations for the given path",
// These are roughly exponential from 0.5 to 300 seconds
Buckets: []float64{0.5, 0.7, 1.1, 1.7, 2.7, 4.2, 6.5, 10, 15, 23, 36, 54, 83, 128, 196, 300},
}, []string{"handler", "code", "method"},
),
InFlight: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "indexerv1_in_flight_requests",
Help: "Gauge of requests in flight",
},
[]string{"handler"},
),
}
var indexerv1wrapper wrapper
62 changes: 61 additions & 1 deletion httptransport/instrumentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import (
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

const (
metricNamespace = `clair`
metricSubsystem = `http`
)

type wrapper struct {
RequestCount *prometheus.CounterVec
RequestSize *prometheus.HistogramVec
Expand All @@ -16,7 +21,62 @@ type wrapper struct {
InFlight *prometheus.GaugeVec
}

func (m *wrapper) init() {
func (m *wrapper) init(name string) {
if m.RequestCount == nil {
m.RequestCount = prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: name + "_request_total",
Help: "A total count of http requests for the given path",
},
[]string{"handler", "code", "method"},
)
}
if m.RequestSize == nil {
m.RequestSize = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: name + "_request_size_bytes",
Help: "Distribution of request sizes for the given path",
},
[]string{"handler", "code", "method"},
)
}
if m.ResponseSize == nil {
m.ResponseSize = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: name + "_response_size_bytes",
Help: "Distribution of response sizes for the given path",
}, []string{"handler", "code", "method"},
)
}
if m.RequestDuration == nil {
m.RequestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: name + "_request_duration_seconds",
Help: "Distribution of request durations for the given path",
// These are roughly exponential from 0.5 to 300 seconds
Buckets: []float64{0.5, 0.7, 1.1, 1.7, 2.7, 4.2, 6.5, 10, 15, 23, 36, 54, 83, 128, 196, 300},
}, []string{"handler", "code", "method"},
)
}
if m.InFlight == nil {
m.InFlight = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: name + "_in_flight_requests",
Help: "Gauge of requests in flight",
},
[]string{"handler"},
)
}
prometheus.MustRegister(m.RequestCount, m.RequestSize, m.ResponseSize, m.RequestDuration, m.InFlight)
}

Expand Down
49 changes: 2 additions & 47 deletions httptransport/matcher_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/google/uuid"
"github.com/ldelossa/responserecorder"
"github.com/prometheus/client_golang/prometheus"
"github.com/quay/claircore"
"github.com/quay/claircore/libvuln/driver"
"github.com/quay/zlog"
Expand Down Expand Up @@ -262,51 +261,7 @@ func (h *MatcherV1) updateOperationHandlerDelete(w http.ResponseWriter, r *http.
}

func init() {
matcherv1wrapper.init()
matcherv1wrapper.init("matcherv1")
}

var matcherv1wrapper = &wrapper{
RequestCount: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "matcherv1_request_total",
Help: "A total count of http requests for the given path",
},
[]string{"handler", "code", "method"},
),
RequestSize: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "matcherv1_request_size_bytes",
Help: "Distribution of request sizes for the given path",
},
[]string{"handler", "code", "method"},
),
ResponseSize: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "matcherv1_response_size_bytes",
Help: "Distribution of response sizes for the given path",
}, []string{"handler", "code", "method"},
),
RequestDuration: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "matcherv1_request_duration_seconds",
Help: "Distribution of request durations for the given path",
}, []string{"handler", "code", "method"},
),
InFlight: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: metricNamespace,
Subsystem: metricSubsystem,
Name: "matcherv1_in_flight_requests",
Help: "Gauge of requests in flight",
},
[]string{"handler"},
),
}
var matcherv1wrapper wrapper
Loading