Skip to content

Commit a34235c

Browse files
Support Warning header aggregation and reporting in crane (#1604)
* Support Warning header aggregation and reporting in crane * write to stderr * revert manifest.go * reduce test warnings * Update cmd/crane/cmd/root.go Co-authored-by: Jon Johnson <jonjohnsonjr@gmail.com> --------- Co-authored-by: Jon Johnson <jonjohnsonjr@gmail.com>
1 parent 46488f7 commit a34235c

File tree

6 files changed

+116
-16
lines changed

6 files changed

+116
-16
lines changed

cmd/crane/cmd/root.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import (
2020
"net/http"
2121
"os"
2222
"path/filepath"
23+
"runtime"
24+
"sort"
25+
"strings"
26+
"sync"
2327

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

51+
wt := &warnTransport{}
52+
4753
root := &cobra.Command{
4854
Use: use,
4955
Short: short,
@@ -90,8 +96,15 @@ func New(use, short string, options []crane.Option) *cobra.Command {
9096
}
9197
}
9298

99+
// Inject our warning-collecting transport.
100+
wt.inner = rt
101+
rt = wt
102+
93103
options = append(options, crane.WithTransport(rt))
94104
},
105+
PersistentPostRun: func(_ *cobra.Command, _ []string) {
106+
wt.Report() // Report any collected warnings.
107+
},
95108
}
96109

97110
root.AddCommand(
@@ -146,3 +159,77 @@ func (ht *headerTransport) RoundTrip(in *http.Request) (*http.Response, error) {
146159
}
147160
return ht.inner.RoundTrip(in)
148161
}
162+
163+
type warnTransport struct {
164+
mu sync.Mutex
165+
warns map[string]struct{}
166+
inner http.RoundTripper
167+
}
168+
169+
func (wt *warnTransport) RoundTrip(in *http.Request) (*http.Response, error) {
170+
resp, err := wt.inner.RoundTrip(in)
171+
if err != nil {
172+
return nil, err
173+
}
174+
175+
for _, wh := range resp.Header.Values("Warning") {
176+
if !strings.HasPrefix(wh, "299 - ") {
177+
// Warning response headers are supposed to have
178+
// warn-code 299 and warn-agent "-"; discard these.
179+
continue
180+
}
181+
start := strings.Index(wh, `"`)
182+
end := strings.LastIndex(wh, `"`)
183+
warn := wh[start+1 : end]
184+
func() {
185+
wt.mu.Lock()
186+
defer wt.mu.Unlock()
187+
if wt.warns == nil {
188+
wt.warns = map[string]struct{}{}
189+
}
190+
wt.warns[warn] = struct{}{}
191+
}()
192+
}
193+
return resp, nil
194+
}
195+
196+
func (wt *warnTransport) Report() {
197+
if wt.warns == nil {
198+
return
199+
}
200+
201+
warns := make([]string, 0, len(wt.warns))
202+
for k := range wt.warns {
203+
warns = append(warns, k)
204+
}
205+
sort.Strings(warns)
206+
prefix := "\033[1;33m[WARNING]\033[0m:"
207+
if nocolor() {
208+
prefix = "[WARNING]:"
209+
}
210+
for _, w := range warns {
211+
// TODO: Consider using logs.Warn here if we move this out of crane.
212+
fmt.Fprintln(os.Stderr, prefix, w)
213+
}
214+
}
215+
216+
func nocolor() bool {
217+
// These adapted from https://github.com/kubernetes/kubernetes/blob/fe91bc257b505eb6057eb50b9c550a7c63e9fb91/staging/src/k8s.io/kubectl/pkg/util/term/term.go
218+
219+
// https://en.wikipedia.org/wiki/Computer_terminal#Dumb_terminals
220+
if os.Getenv("TERM") == "dumb" {
221+
return true
222+
}
223+
224+
// https://no-color.org/
225+
if _, nocolor := os.LookupEnv("NO_COLOR"); nocolor {
226+
return true
227+
}
228+
229+
// On Windows WT_SESSION is set by the modern terminal component.
230+
// Older terminals have poor support for UTF-8, VT escape codes, etc.
231+
if runtime.GOOS == "windows" && os.Getenv("WT_SESSION") == "" {
232+
return true
233+
}
234+
return false
235+
}

cmd/registry/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ func main() {
3838
log.Printf("serving on port %d", porti)
3939
s := &http.Server{
4040
ReadHeaderTimeout: 5 * time.Second, // prevent slowloris, quiet linter
41-
Handler: registry.New(),
41+
Handler: registry.New(
42+
registry.WithWarning(.01, "Congratulations! You've won a lifetime's supply of free image pulls from this in-memory registry!"),
43+
),
4244
}
4345
log.Fatal(s.Serve(listener))
4446
}

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ require (
2121
require (
2222
cloud.google.com/go/compute v1.18.0 // indirect
2323
cloud.google.com/go/compute/metadata v0.2.3 // indirect
24-
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
2524
github.com/Microsoft/go-winio v0.6.0 // indirect
2625
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
2726
github.com/docker/docker-credential-helpers v0.7.0 // indirect
@@ -30,7 +29,7 @@ require (
3029
github.com/gogo/protobuf v1.3.2 // indirect
3130
github.com/golang/protobuf v1.5.3 // indirect
3231
github.com/inconshreveable/mousetrap v1.1.0 // indirect
33-
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
32+
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
3433
github.com/morikuni/aec v1.0.0 // indirect
3534
github.com/pkg/errors v0.9.1 // indirect
3635
github.com/russross/blackfriday/v2 v2.1.0 // indirect

go.sum

Lines changed: 2 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/registry/registry.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
package registry
2525

2626
import (
27+
"fmt"
2728
"log"
29+
"math/rand"
2830
"net/http"
2931
"os"
3032
)
@@ -34,11 +36,21 @@ type registry struct {
3436
blobs blobs
3537
manifests manifests
3638
referrersEnabled bool
39+
warnings map[float64]string
3740
}
3841

3942
// https://docs.docker.com/registry/spec/api/#api-version-check
4043
// https://github.com/opencontainers/distribution-spec/blob/master/spec.md#api-version-check
4144
func (r *registry) v2(resp http.ResponseWriter, req *http.Request) *regError {
45+
if r.warnings != nil {
46+
rnd := rand.Float64()
47+
for prob, msg := range r.warnings {
48+
if prob > rnd {
49+
resp.Header().Add("Warning", fmt.Sprintf(`299 - "%s"`, msg))
50+
}
51+
}
52+
}
53+
4254
if isBlob(req) {
4355
return r.blobs.handle(resp, req)
4456
}
@@ -115,3 +127,12 @@ func WithReferrersSupport(enabled bool) Option {
115127
r.referrersEnabled = enabled
116128
}
117129
}
130+
131+
func WithWarning(prob float64, msg string) Option {
132+
return func(r *registry) {
133+
if r.warnings == nil {
134+
r.warnings = map[float64]string{}
135+
}
136+
r.warnings[prob] = msg
137+
}
138+
}

vendor/modules.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ cloud.google.com/go/compute/internal
44
# cloud.google.com/go/compute/metadata v0.2.3
55
## explicit; go 1.19
66
cloud.google.com/go/compute/metadata
7-
# github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1
8-
## explicit; go 1.16
97
# github.com/Microsoft/go-winio v0.6.0
108
## explicit; go 1.17
119
github.com/Microsoft/go-winio
@@ -91,8 +89,8 @@ github.com/klauspost/compress/zstd/internal/xxhash
9189
# github.com/mitchellh/go-homedir v1.1.0
9290
## explicit
9391
github.com/mitchellh/go-homedir
94-
# github.com/moby/term v0.0.0-20210610120745-9d4ed1856297
95-
## explicit; go 1.13
92+
# github.com/moby/term v0.0.0-20221205130635-1aeaba878587
93+
## explicit; go 1.18
9694
# github.com/morikuni/aec v1.0.0
9795
## explicit
9896
# github.com/opencontainers/go-digest v1.0.0

0 commit comments

Comments
 (0)