Skip to content

Commit

Permalink
fix(sync): added bearer client for sync
Browse files Browse the repository at this point in the history
fixed ping function taking too much time

closes: #2213 #2212

Signed-off-by: Petu Eusebiu <[email protected]>
  • Loading branch information
eusebiu-constantin-petu-dbk committed Feb 9, 2024
1 parent 36e04a4 commit a16ecab
Show file tree
Hide file tree
Showing 21 changed files with 1,086 additions and 134 deletions.
13 changes: 13 additions & 0 deletions examples/.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"distSpecVersion": "1.1.0-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address": "127.0.0.1",
"port": "8080"
},
"log": {
"level": "debug"
}
}
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ Configure each registry sync:
]
},
{
"urls": ["https://docker.io/library"],
"urls": ["https://index.docker.io"],
"onDemand": true, # doesn't have content, don't periodically pull, pull just on demand.
"tlsVerify": true,
"maxRetries": 3,
Expand Down
40 changes: 40 additions & 0 deletions examples/metrics/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# ---
# Stage 1: Install certs, build binary, create default config file
# ---
FROM ghcr.io/project-zot/golang:1.17 AS builder
RUN mkdir -p /go/src/github.com/project-zot/zot
WORKDIR /go/src/github.com/project-zot/zot
COPY . .
RUN make clean binary
RUN echo '{\n\
"storage": {\n\
"rootDirectory": "/var/lib/registry"\n\
},\n\
"http": {\n\
"address": "0.0.0.0",\n\
"port": "5000"\n\
},\n\
"log": {\n\
"level": "debug"\n\
},\n\
"extensions": {\n\
"metrics": {\n\
"enable": true,\n\
"prometheus": {\n\
"path": "/metrics"\n\
}\n\
}\n\
}\n\
}\n' > config.json && cat config.json

# ---
# Stage 2: Final image with nothing but certs, binary, and default config file
# ---
FROM scratch AS final
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /go/src/github.com/project-zot/zot/bin/zot /zot
COPY --from=builder /go/src/github.com/project-zot/zot/config.json /etc/zot/config.json
ENTRYPOINT ["/zot"]
EXPOSE 5000
VOLUME ["/var/lib/registry"]
CMD ["serve", "/etc/zot/config.json"]
32 changes: 32 additions & 0 deletions examples/metrics/Dockerfile-minimal
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# ---
# Stage 1: Install certs, build binary, create default config file
# ---
FROM ghcr.io/project-zot/golang:1.17 AS builder
RUN mkdir -p /go/src/github.com/project-zot/zot
WORKDIR /go/src/github.com/project-zot/zot
COPY . .
RUN make clean binary-minimal
RUN echo '{\n\
"storage": {\n\
"rootDirectory": "/var/lib/registry"\n\
},\n\
"http": {\n\
"address": "0.0.0.0",\n\
"port": "5050"\n\
},\n\
"log": {\n\
"level": "debug"\n\
}\n\
}\n' > config.json && cat config.json

# ---
# Stage 2: Final image with nothing but certs, binary, and default config file
# ---
FROM scratch AS final
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /go/src/github.com/project-zot/zot/bin/zot-minimal /zot
COPY --from=builder /go/src/github.com/project-zot/zot/config.json /etc/zot/config.json
ENTRYPOINT ["/zot"]
EXPOSE 5050
VOLUME ["/var/lib/registry"]
CMD ["serve", "/etc/zot/config.json"]
31 changes: 31 additions & 0 deletions examples/metrics/Dockerfile-zxp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ---
# Stage 1: Build binary, create default config file
# ---
FROM ghcr.io/project-zot/golang:1.17 AS builder
RUN mkdir -p /go/src/github.com/project-zot/zot
WORKDIR /go/src/github.com/project-zot/zot
COPY . .
RUN make clean exporter-minimal
RUN echo '{\n\
"Server": {\n\
"protocol": "http",\n\
"host": "127.0.0.1",\n\
"port": "5050"\n\
},\n\
"Exporter": {\n\
"port": "5051",\n\
"log": {\n\
"level": "debug"\n\
}\n\
}\n\
}\n' > config.json && cat config.json

# ---
# Stage 2: Final image with nothing but binary and default config file
# ---
FROM scratch AS final
COPY --from=builder /go/src/github.com/project-zot/zot/bin/zxp /zxp
COPY --from=builder /go/src/github.com/project-zot/zot/config.json /etc/zxp/config.json
ENTRYPOINT ["/zxp"]
EXPOSE 5051
CMD ["config", "/etc/zxp/config.json"]
27 changes: 27 additions & 0 deletions examples/metrics/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
CONTAINER_RUNTIME := docker

.PHONY: binary-container
binary-container:
${CONTAINER_RUNTIME} build -f Dockerfile -t zot-build:latest ../../.

.PHONY: run-container
run-container:
${CONTAINER_RUNTIME} run --rm --security-opt label=disable -v $$(pwd)/../..:/go/src/github.com/project-zot/zot \
zot-build:latest

.PHONY: binary-minimal-container
binary-minimal-container:
${CONTAINER_RUNTIME} build -f Dockerfile-minimal -t zot-minimal:latest ../../.

.PHONY: run-minimal-container
run-minimal-container:
${CONTAINER_RUNTIME} run --rm --security-opt label=disable -v $$(pwd)/../..:/go/src/github.com/project-zot/zot \
zot-minimal:latest

.PHONY: binary-exporter-container
binary-exporter-container:
${CONTAINER_RUNTIME} build -f Dockerfile-zxp -t zxp:latest ../../.

.PHONY: run-exporter-container
run-exporter-container:
${CONTAINER_RUNTIME} run --rm --security-opt label=disable zxp:latest
2 changes: 1 addition & 1 deletion pkg/api/authn.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ func bearerAuthHandler(ctlr *Controller) mux.MiddlewareFunc {
if err != nil {
ctlr.Log.Error().Err(err).Msg("failed to parse Authorization header")
response.Header().Set("Content-Type", "application/json")
zcommon.WriteJSON(response, http.StatusInternalServerError, apiErr.NewError(apiErr.UNSUPPORTED))
zcommon.WriteJSON(response, http.StatusUnauthorized, apiErr.NewError(apiErr.UNSUPPORTED))

return
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3114,7 +3114,7 @@ func TestBearerAuth(t *testing.T) {
Get(baseURL + "/v2/")
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)

resp, err = resty.R().SetHeader("Authorization",
fmt.Sprintf("Bearer %s", goodToken.AccessToken)).Options(baseURL + "/v2/")
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func (rh *RouteHandler) CheckVersionSupport(response http.ResponseWriter, reques
response.Header().Set(constants.DistAPIVersion, "registry/2.0")
// NOTE: compatibility workaround - return this header in "allowed-read" mode to allow for clients to
// work correctly
if rh.c.Config.HTTP.Auth != nil {
if rh.c.Config.IsBasicAuthnEnabled() || rh.c.Config.IsBearerAuthEnabled() {
// don't send auth headers if request is coming from UI
if request.Header.Get(constants.SessionClientHeaderName) != constants.SessionClientHeaderValue {
if rh.c.Config.HTTP.Auth.Bearer != nil {
Expand Down
58 changes: 1 addition & 57 deletions pkg/common/http_client.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package common

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"io"
"net/http"
"os"
"path"
"path/filepath"

"zotregistry.dev/zot/pkg/log"
)

func GetTLSConfig(certsPath string, caCertPool *x509.CertPool) (*tls.Config, error) {
Expand Down Expand Up @@ -107,57 +101,7 @@ func CreateHTTPClient(verifyTLS bool, host string, certDir string) (*http.Client
}

return &http.Client{
Timeout: httpTimeout,
Transport: htr,
Timeout: httpTimeout,
}, nil
}

func MakeHTTPGetRequest(ctx context.Context, httpClient *http.Client,
username string, password string, resultPtr interface{},
blobURL string, mediaType string, log log.Logger,
) ([]byte, string, int, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, blobURL, nil) //nolint
if err != nil {
return nil, "", 0, err
}

if mediaType != "" {
req.Header.Set("Accept", mediaType)
}

if username != "" && password != "" {
req.SetBasicAuth(username, password)
}

resp, err := httpClient.Do(req)
if err != nil {
log.Error().Str("errorType", TypeOf(err)).
Err(err).Str("blobURL", blobURL).Msg("couldn't get blob")

return nil, "", -1, err
}

defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
log.Error().Str("errorType", TypeOf(err)).
Err(err).Str("blobURL", blobURL).Msg("couldn't get blob")

return nil, "", resp.StatusCode, err
}

if resp.StatusCode != http.StatusOK {
return nil, "", resp.StatusCode, errors.New(string(body)) //nolint:goerr113
}

// read blob
if len(body) > 0 {
err = json.Unmarshal(body, &resultPtr)
if err != nil {
return body, "", resp.StatusCode, err
}
}

return body, resp.Header.Get("Content-Type"), resp.StatusCode, err
}
31 changes: 0 additions & 31 deletions pkg/common/http_client_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package common_test

import (
"context"
"crypto/x509"
"os"
"path"
"testing"

ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey"

"zotregistry.dev/zot/pkg/api"
"zotregistry.dev/zot/pkg/api/config"
"zotregistry.dev/zot/pkg/common"
"zotregistry.dev/zot/pkg/log"
test "zotregistry.dev/zot/pkg/test/common"
)

Expand Down Expand Up @@ -54,30 +49,4 @@ func TestHTTPClient(t *testing.T) {
_, err = common.CreateHTTPClient(true, "localhost", tempDir)
So(err, ShouldNotBeNil)
})

Convey("test MakeHTTPGetRequest() no permissions on key", t, func() {
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)

conf := config.New()
conf.HTTP.Port = port

ctlr := api.NewController(conf)
tempDir := t.TempDir()
err := test.CopyTestKeysAndCerts(tempDir)
So(err, ShouldBeNil)
ctlr.Config.Storage.RootDirectory = tempDir

cm := test.NewControllerManager(ctlr)
cm.StartServer()
defer cm.StopServer()
test.WaitTillServerReady(baseURL)

var resultPtr interface{}
httpClient, err := common.CreateHTTPClient(true, "localhost", tempDir)
So(err, ShouldBeNil)
_, _, _, err = common.MakeHTTPGetRequest(context.Background(), httpClient, "", "",
resultPtr, baseURL+"/v2/", ispec.MediaTypeImageManifest, log.NewLogger("", ""))
So(err, ShouldBeNil)
})
}
2 changes: 2 additions & 0 deletions pkg/extensions/extension_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ func EnableSyncExtension(config *config.Config, metaDB mTypes.MetaDB,

service, err := sync.New(registryConfig, credsPath, tmpDir, storeController, metaDB, log)
if err != nil {
log.Error().Err(err).Msg("failed to initialize sync extension")

return nil, err
}

Expand Down
58 changes: 58 additions & 0 deletions pkg/extensions/sync/httpclient/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package client

import (
"sync"
)

// Key:Value store for bearer tokens, key is namespace, value is token.
// We are storing only pull scoped tokens, the http client is for pulling only.
type TokenCache struct {
entries sync.Map
}

func NewTokenCache() *TokenCache {
return &TokenCache{
entries: sync.Map{},
}
}

func (c *TokenCache) Set(namespace string, token *bearerToken) {
if c == nil || token == nil {
return
}

defer c.prune()

c.entries.Store(namespace, token)
}

func (c *TokenCache) Get(namespace string) *bearerToken {
if c == nil {
return nil
}

val, ok := c.entries.Load(namespace)
if !ok {
return nil
}

bearerToken, ok := val.(*bearerToken)
if !ok {
return nil
}

return bearerToken
}

func (c *TokenCache) prune() {
c.entries.Range(func(key, val any) bool {
bearerToken, ok := val.(*bearerToken)
if ok {
if bearerToken.isExpired() {
c.entries.Delete(key)
}
}

return true
})
}
Loading

0 comments on commit a16ecab

Please sign in to comment.