Skip to content

Commit

Permalink
Support Warning header aggregation and reporting in crane
Browse files Browse the repository at this point in the history
  • Loading branch information
imjasonh committed Mar 23, 2023
1 parent 46488f7 commit 8ab13bf
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 16 deletions.
86 changes: 86 additions & 0 deletions cmd/crane/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import (
"net/http"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"

"github.com/docker/cli/cli/config"
"github.com/google/go-containerregistry/internal/cmd"
Expand All @@ -44,6 +48,8 @@ func New(use, short string, options []crane.Option) *cobra.Command {
ndlayers := false
platform := &platformValue{}

wt := &warnTransport{}

root := &cobra.Command{
Use: use,
Short: short,
Expand Down Expand Up @@ -90,8 +96,15 @@ func New(use, short string, options []crane.Option) *cobra.Command {
}
}

// Inject our warning-collecting transport.
wt.inner = rt
rt = wt

options = append(options, crane.WithTransport(rt))
},
PersistentPostRun: func(_ *cobra.Command, _ []string) {
wt.Report() // Report any collected warnings.
},
}

root.AddCommand(
Expand Down Expand Up @@ -146,3 +159,76 @@ func (ht *headerTransport) RoundTrip(in *http.Request) (*http.Response, error) {
}
return ht.inner.RoundTrip(in)
}

type warnTransport struct {
mu sync.Mutex
warns map[string]struct{}
inner http.RoundTripper
}

func (wt *warnTransport) RoundTrip(in *http.Request) (*http.Response, error) {
resp, err := wt.inner.RoundTrip(in)
if err != nil {
return nil, err
}

for _, wh := range resp.Header.Values("Warning") {
if !strings.HasPrefix(wh, "299 - ") {
// Warning response headers are supposed to have
// warn-code 299 and warn-agent "-"; discard these.
continue
}
start := strings.Index(wh, `"`)
end := strings.LastIndex(wh, `"`)
warn := wh[start+1 : end]
func() {
wt.mu.Lock()
defer wt.mu.Unlock()
if wt.warns == nil {
wt.warns = map[string]struct{}{}
}
wt.warns[warn] = struct{}{}
}()
}
return resp, nil
}

func (wt *warnTransport) Report() {
if wt.warns == nil {
return
}

warns := make([]string, 0, len(wt.warns))
for k := range wt.warns {
warns = append(warns, k)
}
sort.Strings(warns)
prefix := "\033[1;33m[WARNING]\033[0m:"
if nocolor() {
prefix = "[WARNING]:"
}
for _, w := range warns {
fmt.Println(prefix, w)
}
}

func nocolor() bool {
// These adapted from https://github.com/kubernetes/kubernetes/blob/fe91bc257b505eb6057eb50b9c550a7c63e9fb91/staging/src/k8s.io/kubectl/pkg/util/term/term.go

// https://en.wikipedia.org/wiki/Computer_terminal#Dumb_terminals
if os.Getenv("TERM") == "dumb" {
return true
}

// https://no-color.org/
if _, nocolor := os.LookupEnv("NO_COLOR"); nocolor {
return true
}

// On Windows WT_SESSION is set by the modern terminal component.
// Older terminals have poor support for UTF-8, VT escape codes, etc.
if runtime.GOOS == "windows" && os.Getenv("WT_SESSION") == "" {
return true
}
return false
}
6 changes: 5 additions & 1 deletion cmd/registry/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ func main() {
log.Printf("serving on port %d", porti)
s := &http.Server{
ReadHeaderTimeout: 5 * time.Second, // prevent slowloris, quiet linter
Handler: registry.New(),
Handler: registry.New(
registry.WithWarning(1.0, "This registry is cool."),
registry.WithWarning(0.6, "60% of the time, it works every time."),
registry.WithWarning(0.1, "Today is your lucky day!"),
),
}
log.Fatal(s.Serve(listener))
}
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ require (
require (
cloud.google.com/go/compute v1.18.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
Expand All @@ -30,7 +29,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
Expand Down
11 changes: 2 additions & 9 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions pkg/registry/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"io"
"log"
"math/rand"
"net/http"
"sort"
"strconv"
Expand Down Expand Up @@ -89,9 +90,21 @@ func isReferrers(req *http.Request) bool {
return elems[len(elems)-2] == "referrers"
}

var msgs = []string{
"This image is deprecated and will be removed in 17 days.",
"This image has 3 critical CVEs -- consider upgrading to :v1.2.5 as soon as possible",
"Your auth token will expire in 30 seconds.",
"Did you know: The woolly mammoth was still around when the pyramids were being built.",
}

// https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pulling-an-image-manifest
// https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pushing-an-image
func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regError {

if rand.Intn(5) == 1 {
resp.Header().Set("Warning", msgs[rand.Intn(len(msgs))])
}

elem := strings.Split(req.URL.Path, "/")
elem = elem[1:]
target := elem[len(elem)-1]
Expand Down
21 changes: 21 additions & 0 deletions pkg/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
package registry

import (
"fmt"
"log"
"math/rand"
"net/http"
"os"
)
Expand All @@ -34,11 +36,21 @@ type registry struct {
blobs blobs
manifests manifests
referrersEnabled bool
warnings map[float64]string
}

// https://docs.docker.com/registry/spec/api/#api-version-check
// https://github.com/opencontainers/distribution-spec/blob/master/spec.md#api-version-check
func (r *registry) v2(resp http.ResponseWriter, req *http.Request) *regError {
if r.warnings != nil {
rnd := rand.Float64()
for prob, msg := range r.warnings {
if prob > rnd {
resp.Header().Add("Warning", fmt.Sprintf(`299 - "%s"`, msg))
}
}
}

if isBlob(req) {
return r.blobs.handle(resp, req)
}
Expand Down Expand Up @@ -115,3 +127,12 @@ func WithReferrersSupport(enabled bool) Option {
r.referrersEnabled = enabled
}
}

func WithWarning(prob float64, msg string) Option {
return func(r *registry) {
if r.warnings == nil {
r.warnings = map[float64]string{}
}
r.warnings[prob] = msg
}
}
6 changes: 2 additions & 4 deletions vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ cloud.google.com/go/compute/internal
# cloud.google.com/go/compute/metadata v0.2.3
## explicit; go 1.19
cloud.google.com/go/compute/metadata
# github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1
## explicit; go 1.16
# github.com/Microsoft/go-winio v0.6.0
## explicit; go 1.17
github.com/Microsoft/go-winio
Expand Down Expand Up @@ -91,8 +89,8 @@ github.com/klauspost/compress/zstd/internal/xxhash
# github.com/mitchellh/go-homedir v1.1.0
## explicit
github.com/mitchellh/go-homedir
# github.com/moby/term v0.0.0-20210610120745-9d4ed1856297
## explicit; go 1.13
# github.com/moby/term v0.0.0-20221205130635-1aeaba878587
## explicit; go 1.18
# github.com/morikuni/aec v1.0.0
## explicit
# github.com/opencontainers/go-digest v1.0.0
Expand Down

0 comments on commit 8ab13bf

Please sign in to comment.