autoregistry
This commit is contained in:
parent
1f86f1a3e6
commit
426a2e7db8
29
autoregistry/README.md
Normal file
29
autoregistry/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
|
||||
# Introduction
|
||||
|
||||
When developing with a local k8s cluster, images must be made available to a local registry.
|
||||
This involves tagging the images (e.g. `localhost:5000/alpine`) and then pushing them.
|
||||
Tagging is usually done transparently by e.g. `docker compose build` and it is easy to forget
|
||||
to push the images.
|
||||
|
||||
The autoproxy solves this problem by proxying for a local container registry. When an image is
|
||||
pulled it checkes whether or not the image is available in the container registry. If not, then
|
||||
it looks for the requested image in docker and does a push automatically. If the image is
|
||||
available in the registry the autoproxy checks is the images is the same as on the registry and
|
||||
if not it pushes it.
|
||||
|
||||
# Usage
|
||||
|
||||
A test setup is in the `compose.yaml` file. In this setup:
|
||||
* a backend registry runs which is just a regular local container registry
|
||||
* the frontend service is the proxy for the backend
|
||||
|
||||
In the example setup images tagged with `localhost:5000/...` will automatically be
|
||||
pushed by the proxy in case they are requested.
|
||||
|
||||
# k3d
|
||||
|
||||
```
|
||||
k3d cluster create dev --registry-config registries.yaml
|
||||
```
|
13
autoregistry/bigmf/Dockerfile
Normal file
13
autoregistry/bigmf/Dockerfile
Normal file
@ -0,0 +1,13 @@
|
||||
FROM alpine:3.20.0
|
||||
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
||||
RUN dd if=/dev/urandom bs=1M count=50 of=x.img
|
40
autoregistry/compose.yaml
Normal file
40
autoregistry/compose.yaml
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
name: registry
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: registry:2.8.3
|
||||
restart: always
|
||||
volumes:
|
||||
- registry:/var/lib/registry
|
||||
# only for testing.
|
||||
#ports:
|
||||
# for debugging only, not requireed to be accessioble
|
||||
# - 4999:5000
|
||||
|
||||
frontend:
|
||||
image: regproxy
|
||||
build:
|
||||
context: proxy
|
||||
restart: always
|
||||
#network_mode: host
|
||||
command:
|
||||
- localhost
|
||||
- "5000"
|
||||
- http://backend:5000
|
||||
ports:
|
||||
# run images with localhost:5001
|
||||
- 5000:5000
|
||||
volumes:
|
||||
- //var/run:/var/run
|
||||
|
||||
# an image with a lot of big layers to verify the mechanism.
|
||||
bigmf:
|
||||
build:
|
||||
context: bigmf
|
||||
image: localhost:5000/bigmf
|
||||
profiles:
|
||||
- testing
|
||||
|
||||
volumes:
|
||||
registry:
|
16
autoregistry/proxy/Dockerfile
Normal file
16
autoregistry/proxy/Dockerfile
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
FROM alpine:3.20.0 as builder
|
||||
|
||||
RUN apk update && apk add go
|
||||
RUN mkdir /opt/proxy
|
||||
COPY go.* /opt/proxy/
|
||||
WORKDIR /opt/proxy
|
||||
RUN go mod download
|
||||
COPY *.go /opt/proxy/
|
||||
RUN go build
|
||||
|
||||
FROM scratch
|
||||
COPY --from=builder /lib/ld-musl-x86_64.so.1 /lib/
|
||||
COPY --from=builder /opt/proxy/proxy /opt/proxy/proxy
|
||||
|
||||
ENTRYPOINT ["/opt/proxy/proxy"]
|
197
autoregistry/proxy/docker.go
Normal file
197
autoregistry/proxy/docker.go
Normal file
@ -0,0 +1,197 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/client"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// based on an example from https://www.loginradius.com/blog/engineering/build-push-docker-images-golang/
|
||||
|
||||
type ErrorLine struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDetail ErrorDetail `json:"errorDetail"`
|
||||
}
|
||||
|
||||
type ErrorDetail struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// docker errors are so critical that the safest thing is to just exit and
|
||||
// get restarted.
|
||||
func exitOnDockerError() {
|
||||
if err := recover(); err != nil {
|
||||
log.Fatalf("Exiting the proxy because of a docker error\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func print(rd io.Reader) error {
|
||||
var lastLine string
|
||||
|
||||
scanner := bufio.NewScanner(rd)
|
||||
for scanner.Scan() {
|
||||
lastLine = scanner.Text()
|
||||
log.Println(scanner.Text())
|
||||
}
|
||||
|
||||
errLine := &ErrorLine{}
|
||||
json.Unmarshal([]byte(lastLine), errLine)
|
||||
if errLine.Error != "" {
|
||||
return errors.New(errLine.Error)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDockerClient() *client.Client {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cli
|
||||
}
|
||||
|
||||
func GetDockerImageId(registry, image_name, tag_name string) (imageId, imageName string) {
|
||||
defer exitOnDockerError()
|
||||
full_image := registry + "/" + image_name
|
||||
if strings.HasPrefix(tag_name, "sha256:") || strings.HasPrefix(tag_name, "sha512:") {
|
||||
full_image += "@" + tag_name
|
||||
} else {
|
||||
full_image += ":" + tag_name
|
||||
}
|
||||
cli := GetDockerClient()
|
||||
|
||||
images, err := cli.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, image_obj := range images {
|
||||
for _, tag := range image_obj.RepoTags {
|
||||
if tag == full_image {
|
||||
return image_obj.ID, full_image
|
||||
}
|
||||
}
|
||||
for _, tag := range image_obj.RepoDigests {
|
||||
if tag == full_image {
|
||||
// now we need to find an image tag that is in the
|
||||
// registry and that has a regular tag so we can push it.
|
||||
for _, pushTag := range image_obj.RepoTags {
|
||||
if strings.HasPrefix(pushTag, registry+"/") {
|
||||
return image_obj.ID, pushTag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
var lockChannel = make(chan string, 1)
|
||||
|
||||
func PushDockerImage(full_image string) {
|
||||
defer exitOnDockerError()
|
||||
log.Printf("Image %s is unavailable in backend", full_image)
|
||||
|
||||
// only do one push at a time to be sure.
|
||||
select {
|
||||
case lockChannel <- "locking":
|
||||
{
|
||||
defer func() {
|
||||
<-lockChannel
|
||||
}()
|
||||
}
|
||||
case <-time.After(2 * time.Minute):
|
||||
{
|
||||
// kubernetes will retry anyway when a pull fails.
|
||||
log.Printf("Lock not acquired within the timout, client will have to retry pull later")
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Printf("Got lock for pushing %s, starting push", full_image)
|
||||
|
||||
cli := GetDockerClient()
|
||||
closer, err := cli.ImagePush(context.Background(), full_image,
|
||||
image.PushOptions{
|
||||
RegistryAuth: "abc123", // dummy
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer closer.Close()
|
||||
err = print(closer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocker(host, port string) {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
images, err := cli.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, image := range images {
|
||||
for _, tag := range image.RepoTags {
|
||||
log.Println(image.ID, tag)
|
||||
}
|
||||
}
|
||||
|
||||
// Now push an image
|
||||
closer, err := cli.ImagePush(context.Background(), host+":"+port+"/alpine:latest",
|
||||
image.PushOptions{
|
||||
All: false,
|
||||
RegistryAuth: "abc123", // dummy
|
||||
PrivilegeFunc: nil,
|
||||
Platform: nil,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
err = print(closer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// parsing image and tag from a context path
|
||||
|
||||
path := "v2/alpine/manifests/latest"
|
||||
r := regexp.MustCompile("^v2/(.*)/manifests/([^/]+)$")
|
||||
matches := r.FindStringSubmatch(path)
|
||||
fmt.Println("Matches ", len(matches), matches)
|
||||
if len(matches) == 3 {
|
||||
fmt.Println("Match ", matches[1], matches[2])
|
||||
} else {
|
||||
fmt.Println("No match", matches)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func CheckDockerAvailable() {
|
||||
log.Printf("Checking connection to docker daemon\n")
|
||||
client := GetDockerClient()
|
||||
_, err := client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
37
autoregistry/proxy/go.mod
Normal file
37
autoregistry/proxy/go.mod
Normal file
@ -0,0 +1,37 @@
|
||||
module proxy
|
||||
|
||||
require (
|
||||
github.com/docker/docker v27.0.3+incompatible
|
||||
github.com/go-resty/resty/v2 v2.11.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
)
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.9
|
155
autoregistry/proxy/go.sum
Normal file
155
autoregistry/proxy/go.sum
Normal file
@ -0,0 +1,155 @@
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=
|
||||
github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
|
||||
github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
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=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
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/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=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
98
autoregistry/proxy/main.go
Normal file
98
autoregistry/proxy/main.go
Normal file
@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
type AutoRegistry struct {
|
||||
host string
|
||||
port string
|
||||
backendRegistryUrl string
|
||||
reverseProxy *httputil.ReverseProxy
|
||||
}
|
||||
|
||||
func NewAutoRegistry(host, port, backendRegistryUrl string) *AutoRegistry {
|
||||
proxy := AutoRegistry{
|
||||
host: host, port: port,
|
||||
backendRegistryUrl: backendRegistryUrl,
|
||||
}
|
||||
url, err := url.Parse(backendRegistryUrl)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
proxy.reverseProxy = httputil.NewSingleHostReverseProxy(url)
|
||||
return &proxy
|
||||
}
|
||||
|
||||
func (autoProxy *AutoRegistry) setupHandler() {
|
||||
http.HandleFunc("/", autoProxy.requestHandler())
|
||||
}
|
||||
|
||||
// ProxyRequestHandler handles the http request using proxy
|
||||
func (autoProxy *AutoRegistry) requestHandler() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
autoProxy.pushImageIfNeeded(r, autoProxy.host+":"+autoProxy.port)
|
||||
autoProxy.reverseProxy.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (autoProxy *AutoRegistry) pushImageIfNeeded(r *http.Request, registry string) {
|
||||
// below will catch error but can return old image to the user.
|
||||
// This will lead to a lot of troubleshooting. Better is to fail the request to the client
|
||||
// when there is a problem with docker.
|
||||
//defer func() {
|
||||
// if r := recover(); r != nil {
|
||||
// log.Println("ERROR: Docker interaction failed: ", r)
|
||||
// }
|
||||
//}()
|
||||
|
||||
log.Printf("Got request: %s, url: %s\n", r.Method, r.URL.Path)
|
||||
if r.Method != "HEAD" && r.Method != "GET" {
|
||||
return
|
||||
}
|
||||
image_name, tag_name, ok := ParseImage(r.URL.Path)
|
||||
if !ok {
|
||||
// not a manifest request
|
||||
return
|
||||
}
|
||||
docker_id, push_tag := GetDockerImageId(registry, image_name, tag_name)
|
||||
log.Printf("Docker id %s\n", docker_id)
|
||||
if docker_id == "" {
|
||||
return
|
||||
}
|
||||
registry_id := GetRegistryId(autoProxy.backendRegistryUrl, image_name, tag_name)
|
||||
if docker_id == registry_id {
|
||||
log.Printf("Image %s:%s is up to date in registry\n", image_name, tag_name)
|
||||
}
|
||||
log.Printf("image %s:%s is not up to date in registry, pushing it\n", image_name, tag_name)
|
||||
PushDockerImage(push_tag)
|
||||
}
|
||||
|
||||
func printHelp() {
|
||||
fmt.Println("Usage: proxy <host> <port> <backendUrl>")
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// make sure we can connect to docker and fail immediately otherwise
|
||||
CheckDockerAvailable()
|
||||
|
||||
if len(os.Args) != 4 {
|
||||
printHelp()
|
||||
}
|
||||
host := os.Args[1]
|
||||
port := os.Args[2]
|
||||
backendRegistry := os.Args[3]
|
||||
|
||||
log.Printf("Running with: host %s port %s backend %s\n", host, port, backendRegistry)
|
||||
|
||||
proxy := NewAutoRegistry(host, port, backendRegistry)
|
||||
proxy.setupHandler()
|
||||
// handle all requests to your server using the proxy
|
||||
log.Fatal(http.ListenAndServe(":"+port, nil))
|
||||
}
|
79
autoregistry/proxy/registry.go
Normal file
79
autoregistry/proxy/registry.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Manifest struct {
|
||||
Config struct {
|
||||
Digest string
|
||||
}
|
||||
}
|
||||
|
||||
func GetRegistryId(registry_url, image_name, tag_name string) string {
|
||||
client := resty.New()
|
||||
client.SetDebug(false)
|
||||
resp, err := client.R().
|
||||
EnableTrace().
|
||||
SetResult(Manifest{}).
|
||||
ForceContentType("application/json").
|
||||
SetHeader("Accept", "application/vnd.docker.distribution.manifest.v2+json").
|
||||
Get(registry_url + "/v2/" + image_name + "/manifests/" + tag_name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
manifest := resp.Result().(*Manifest)
|
||||
return manifest.Config.Digest
|
||||
}
|
||||
|
||||
func ParseImage(path string) (image_name string, tag_name string, ok bool) {
|
||||
r := regexp.MustCompile("^/?v2/(.*)/manifests/([^/]+)$")
|
||||
matches := r.FindStringSubmatch(path)
|
||||
if len(matches) == 3 {
|
||||
return matches[1], matches[2], true
|
||||
}
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
func testRegistry() {
|
||||
client := resty.New()
|
||||
client.SetDebug(true)
|
||||
resp, err := client.R().
|
||||
EnableTrace().
|
||||
SetResult(Manifest{}).
|
||||
ForceContentType("application/json").
|
||||
SetHeader("Accept", "application/vnd.docker.distribution.manifest.v2+json").
|
||||
Get("http://host.docker.internal:4999/v2/alpine/manifests/latest")
|
||||
|
||||
fmt.Println("err ", err)
|
||||
|
||||
fmt.Printf("results: %v\n", resp.Result().(*Manifest))
|
||||
|
||||
fmt.Println("Response Info:")
|
||||
fmt.Println(" Error :", err)
|
||||
fmt.Println(" Status Code:", resp.StatusCode())
|
||||
fmt.Println(" Status :", resp.Status())
|
||||
fmt.Println(" Proto :", resp.Proto())
|
||||
fmt.Println(" Time :", resp.Time())
|
||||
fmt.Println(" Received At:", resp.ReceivedAt())
|
||||
fmt.Println(" Body :\n", resp)
|
||||
fmt.Println()
|
||||
|
||||
// Explore trace info
|
||||
fmt.Println("Request Trace Info:")
|
||||
ti := resp.Request.TraceInfo()
|
||||
fmt.Println(" DNSLookup :", ti.DNSLookup)
|
||||
fmt.Println(" ConnTime :", ti.ConnTime)
|
||||
fmt.Println(" TCPConnTime :", ti.TCPConnTime)
|
||||
fmt.Println(" TLSHandshake :", ti.TLSHandshake)
|
||||
fmt.Println(" ServerTime :", ti.ServerTime)
|
||||
fmt.Println(" ResponseTime :", ti.ResponseTime)
|
||||
fmt.Println(" TotalTime :", ti.TotalTime)
|
||||
fmt.Println(" IsConnReused :", ti.IsConnReused)
|
||||
fmt.Println(" IsConnWasIdle :", ti.IsConnWasIdle)
|
||||
fmt.Println(" ConnIdleTime :", ti.ConnIdleTime)
|
||||
fmt.Println(" RequestAttempt:", ti.RequestAttempt)
|
||||
fmt.Println(" RemoteAddr :", ti.RemoteAddr.String())
|
||||
}
|
8
autoregistry/registries.yaml
Normal file
8
autoregistry/registries.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
# k3d registries.yaml to allow k3d to use localhost:5000 for pulling images.
|
||||
|
||||
mirrors:
|
||||
localhost:5000:
|
||||
endpoint:
|
||||
- http://host.k3d.internal:5000
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user