0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

feat(ui): package zui within zot binary (#1161)

(cherry picked from commit d557da0baba819b7cd7e6b5941528776e125ac6d)

build(ui): fix stacker builds


(cherry picked from commit ba25daf02b4a9bc7ee1cb6f84b7a6b096ca7d61f)

build(ui): various fixes

- Fix metrics endpoint
- Fix unit tests unit tests
- Make the ui build optional in the makefile
before the linter lint runs in the golangci-lint workflow
- Do not attempt to include UI routes if search is enabled
- Fix authorization for search endpoint



fix: use zot tag in ui make target


(cherry picked from commit 2a6882fa23f06b2d68c6c299773a6ff50bf90e78)

Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
Signed-off-by: Catalin Hofnar <catalin.hofnar@gmail.com>
Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
Co-authored-by: Ramkumar Chinchani <rchincha@cisco.com>
This commit is contained in:
Andrei Aaron 2023-02-11 00:52:54 +02:00 committed by GitHub
parent d12836e69c
commit c0aaca8ed1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 189 additions and 37 deletions

View file

@ -41,6 +41,19 @@ jobs:
go-version: 1.19.x go-version: 1.19.x
- name: Check out source code - name: Check out source code
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Push release tag to zui
if: github.event_name == 'release' && github.event.action == 'published' && matrix.os == 'linux' && matrix.arch == 'amd64'
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.ZUI_TOKEN }}
repo: zui
owner: project-zot
tag: ${{ github.event.release.tag_name }}
name: ${{ github.event.release.name }}
body: ${{ github.event.release.body }}
commit: main
- name: Cache go dependencies - name: Cache go dependencies
id: cache-go-dependencies id: cache-go-dependencies
uses: actions/cache@v3 uses: actions/cache@v3
@ -105,7 +118,7 @@ jobs:
make OS=$OS ARCH=$ARCH make OS=$OS ARCH=$ARCH
sudo env "PATH=$PATH" make privileged-test sudo env "PATH=$PATH" make privileged-test
else else
make OS=$OS ARCH=$ARCH binary binary-minimal binary-debug cli bench exporter-minimal make OS=$OS ARCH=$ARCH binary binary-minimal binary-debug binary-ui cli bench exporter-minimal
fi fi
env: env:
S3MOCK_ENDPOINT: localhost:4566 S3MOCK_ENDPOINT: localhost:4566

3
.gitignore vendored
View file

@ -11,6 +11,9 @@
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
# Output of the UI build
pkg/extensions/build/
# Tooling used for blackbox testing # Tooling used for blackbox testing
hack/ hack/
.stacker/ .stacker/

View file

@ -32,7 +32,7 @@ hyphen:= -
extended-name:= extended-name:=
.PHONY: all .PHONY: all
all: modcheck swagger binary binary-minimal binary-debug cli bench exporter-minimal verify-config test covhtml check check-gh-actions all: modcheck swagger binary binary-minimal binary-debug binary-ui cli bench exporter-minimal verify-config test covhtml check check-gh-actions
.PHONY: modcheck .PHONY: modcheck
modcheck: modcheck:
@ -45,7 +45,7 @@ ifdef EXTENSIONS
endif endif
.PHONY: build-metadata .PHONY: build-metadata
build-metadata: build-metadata: $(if $(findstring ui,$(EXTENSIONS)), ui)
echo "Imports: \n" echo "Imports: \n"
go list -tags $(EXTENSIONS) -f '{{ join .Imports "\n" }}' ./... | sort -u go list -tags $(EXTENSIONS) -f '{{ join .Imports "\n" }}' ./... | sort -u
echo "\n Files: \n" echo "\n Files: \n"
@ -57,13 +57,19 @@ binary-minimal: modcheck build-metadata
env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-$(OS)-$(ARCH)-minimal -buildmode=pie -tags containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.ReleaseTag=${RELEASE_TAG} -X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=minimal -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zot env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-$(OS)-$(ARCH)-minimal -buildmode=pie -tags containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.ReleaseTag=${RELEASE_TAG} -X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=minimal -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zot
.PHONY: binary .PHONY: binary
binary: $(if $(findstring ui,$(EXTENSIONS)), ui)
binary: modcheck create-name build-metadata binary: modcheck create-name build-metadata
env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-$(OS)-$(ARCH) -buildmode=pie -tags $(EXTENSIONS),containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.ReleaseTag=${RELEASE_TAG} -X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=$(extended-name) -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zot env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-$(OS)-$(ARCH) -buildmode=pie -tags $(EXTENSIONS),containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.ReleaseTag=${RELEASE_TAG} -X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=$(extended-name) -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zot
.PHONY: binary-debug .PHONY: binary-debug
binary-debug: $(if $(findstring ui,$(EXTENSIONS)), ui)
binary-debug: modcheck swagger create-name build-metadata binary-debug: modcheck swagger create-name build-metadata
env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-$(OS)-$(ARCH)-debug -buildmode=pie -tags $(EXTENSIONS),debug,containers_image_openpgp -v -gcflags all='-N -l' -ldflags "-X zotregistry.io/zot/pkg/api/config.ReleaseTag=${RELEASE_TAG} -X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=$(extended-name) -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION}" ./cmd/zot env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-$(OS)-$(ARCH)-debug -buildmode=pie -tags $(EXTENSIONS),debug,containers_image_openpgp -v -gcflags all='-N -l' -ldflags "-X zotregistry.io/zot/pkg/api/config.ReleaseTag=${RELEASE_TAG} -X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=$(extended-name) -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION}" ./cmd/zot
.PHONY: binary-ui
binary-ui: modcheck create-name build-metadata ui
env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-$(OS)-$(ARCH)-ui -buildmode=pie -tags $(EXTENSIONS),ui,containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=$(extended-name) -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zot
.PHONY: cli .PHONY: cli
cli: modcheck create-name build-metadata cli: modcheck create-name build-metadata
env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zli-$(OS)-$(ARCH) -buildmode=pie -tags $(EXTENSIONS),search,containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=$(extended-name) -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zli env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zli-$(OS)-$(ARCH) -buildmode=pie -tags $(EXTENSIONS),search,containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=$(extended-name) -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zli
@ -78,6 +84,7 @@ exporter-minimal: modcheck build-metadata
env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zxp-$(OS)-$(ARCH) -buildmode=pie -tags containers_image_openpgp -v -trimpath ./cmd/zxp env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zxp-$(OS)-$(ARCH) -buildmode=pie -tags containers_image_openpgp -v -trimpath ./cmd/zxp
.PHONY: test .PHONY: test
test: $(if $(findstring ui,$(EXTENSIONS)), ui)
test: check-skopeo $(TESTDATA) $(NOTATION) $(ORAS) test: check-skopeo $(TESTDATA) $(NOTATION) $(ORAS)
go test -failfast -tags $(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-extended.txt -covermode=atomic ./... go test -failfast -tags $(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-extended.txt -covermode=atomic ./...
go test -failfast -tags containers_image_openpgp -v -trimpath -race -cover -coverpkg ./... -coverprofile=coverage-minimal.txt -covermode=atomic ./... go test -failfast -tags containers_image_openpgp -v -trimpath -race -cover -coverpkg ./... -coverprofile=coverage-minimal.txt -covermode=atomic ./...
@ -87,6 +94,7 @@ test: check-skopeo $(TESTDATA) $(NOTATION) $(ORAS)
go test -failfast -tags stress,$(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m ./pkg/cli/stress_test.go go test -failfast -tags stress,$(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m ./pkg/cli/stress_test.go
.PHONY: privileged-test .PHONY: privileged-test
privileged-test: $(if $(findstring ui,$(EXTENSIONS)), ui)
privileged-test: check-skopeo $(TESTDATA) $(NOTATION) privileged-test: check-skopeo $(TESTDATA) $(NOTATION)
go test -failfast -tags needprivileges,$(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-dev-needprivileges.txt -covermode=atomic ./pkg/storage/... ./pkg/cli/... -run ^TestElevatedPrivileges go test -failfast -tags needprivileges,$(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-dev-needprivileges.txt -covermode=atomic ./pkg/storage/... ./pkg/cli/... -run ^TestElevatedPrivileges
@ -151,12 +159,16 @@ $(GOLINTER):
$(GOLINTER) version $(GOLINTER) version
.PHONY: check .PHONY: check
check: $(if $(findstring ui,$(EXTENSIONS)), ui)
check: ./golangcilint.yaml $(GOLINTER) check: ./golangcilint.yaml $(GOLINTER)
mkdir -p pkg/extensions/build; touch pkg/extensions/build/.empty
$(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags containers_image_openpgp ./... $(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags containers_image_openpgp ./...
$(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags $(EXTENSIONS),containers_image_openpgp ./... $(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags $(EXTENSIONS),containers_image_openpgp ./...
$(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags $(EXTENSIONS),containers_image_openpgp,ui,debug ./...
$(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags dev,containers_image_openpgp ./... $(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags dev,containers_image_openpgp ./...
$(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags dev,$(EXTENSIONS),containers_image_openpgp ./... $(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags dev,$(EXTENSIONS),containers_image_openpgp ./...
$(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags stress,$(EXTENSIONS),containers_image_openpgp ./... $(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags stress,$(EXTENSIONS),containers_image_openpgp ./...
rm pkg/extensions/build/.empty
swagger/docs.go: swagger/docs.go:
swag -v || go install github.com/swaggo/swag/cmd/swag@1.6.3 swag -v || go install github.com/swaggo/swag/cmd/swag@1.6.3
@ -204,6 +216,7 @@ clean:
rm -rf hack rm -rf hack
rm -rf test/data/zot-test rm -rf test/data/zot-test
rm -rf test/data/zot-cve-test rm -rf test/data/zot-cve-test
rm -rf pkg/extensions/build
.PHONY: run .PHONY: run
run: binary test run: binary test
@ -262,12 +275,12 @@ run-container:
.PHONY: binary-stacker .PHONY: binary-stacker
binary-stacker: binary-stacker:
${STACKER} build \ ${STACKER} --debug build \
-f build/stacker.yaml \ -f build/stacker.yaml \
--substitute COMMIT=$(PWD) \ --substitute PWD=$$PWD \
--substitute OS=$(OS) \ --substitute COMMIT=$$COMMIT \
--substitute ARCH=$(ARCH) \ --substitute ARCH=$$ARCH \
--substitute PWD=$(PWD) --substitute OS=$$OS
.PHONY: image .PHONY: image
image: image:
@ -369,3 +382,20 @@ $(COSIGN):
mkdir -p $(TOOLSDIR)/bin mkdir -p $(TOOLSDIR)/bin
curl -fsSL https://github.com/sigstore/cosign/releases/download/v1.13.0/cosign-linux-amd64 -o $@; \ curl -fsSL https://github.com/sigstore/cosign/releases/download/v1.13.0/cosign-linux-amd64 -o $@; \
chmod +x $@ chmod +x $@
.PHONY: ui
ui:
pwd=$$(pwd);\
tdir=$$(mktemp -d);\
cd $$tdir;\
if [ -z $(RELEASE_UI) ]; then\
git clone https://github.com/project-zot/zui.git;\
else\
git clone --depth 1 --branch $(RELEASE_TAG) https://github.com/project-zot/zui.git;\
fi;\
cd zui;\
npm install;\
npm run build;\
cd $$pwd;\
rm -rf ./pkg/extensions/build;\
cp -R $$tdir/zui/build ./pkg/extensions/;

View file

@ -45,7 +45,7 @@ build:
arch: ${{ARCH}} arch: ${{ARCH}}
from: from:
type: docker type: docker
url: docker://gcr.io/distroless/base:latest-${{ARCH}} url: docker://zothub.io/c3/base:jammy
overlay_dirs: overlay_dirs:
- source: ../.build/${{REPO_NAME}}/binary - source: ../.build/${{REPO_NAME}}/binary
dest: /usr/local/bin dest: /usr/local/bin

23
examples/config-ui.json Normal file
View file

@ -0,0 +1,23 @@
{
"distSpecVersion": "1.1.0-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address": "0.0.0.0",
"port": "8080"
},
"log": {
"level": "debug"
},
"extensions": {
"search": {
"cve": {
"updateInterval": "2h"
}
},
"ui": {
"enable": true
}
}
}

View file

@ -58,7 +58,8 @@ func allowedMethods(method string) []string {
} }
func (rh *RouteHandler) SetupRoutes() { func (rh *RouteHandler) SetupRoutes() {
rh.c.Router.Use(AuthHandler(rh.c)) prefixedRouter := rh.c.Router.PathPrefix(constants.RoutePrefix).Subrouter()
prefixedRouter.Use(AuthHandler(rh.c))
// authz is being enabled if AccessControl is specified // authz is being enabled if AccessControl is specified
// if Authn is not present AccessControl will have only default policies // if Authn is not present AccessControl will have only default policies
if rh.c.Config.AccessControl != nil && !isBearerAuthEnabled(rh.c.Config) { if rh.c.Config.AccessControl != nil && !isBearerAuthEnabled(rh.c.Config) {
@ -68,11 +69,10 @@ func (rh *RouteHandler) SetupRoutes() {
rh.c.Log.Info().Msg("default policy only access control is being enabled") rh.c.Log.Info().Msg("default policy only access control is being enabled")
} }
rh.c.Router.Use(AuthzHandler(rh.c)) prefixedRouter.Use(AuthzHandler(rh.c))
} }
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#endpoints // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#endpoints
prefixedRouter := rh.c.Router.PathPrefix(constants.RoutePrefix).Subrouter()
{ {
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", zreg.NameRegexp.String()), prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", zreg.NameRegexp.String()),
rh.ListTags).Methods(allowedMethods("GET")...) rh.ListTags).Methods(allowedMethods("GET")...)
@ -116,7 +116,7 @@ func (rh *RouteHandler) SetupRoutes() {
constants.ArtifactSpecRoutePrefix, zreg.NameRegexp.String()), rh.GetOrasReferrers).Methods("GET") constants.ArtifactSpecRoutePrefix, zreg.NameRegexp.String()), rh.GetOrasReferrers).Methods("GET")
// swagger // swagger
debug.SetupSwaggerRoutes(rh.c.Config, rh.c.Router, rh.c.Log) debug.SetupSwaggerRoutes(rh.c.Config, rh.c.Router, AuthHandler(rh.c), rh.c.Log)
// Setup Extensions Routes // Setup Extensions Routes
if rh.c.Config != nil { if rh.c.Config != nil {
@ -125,9 +125,10 @@ func (rh *RouteHandler) SetupRoutes() {
prefixedRouter.HandleFunc("/metrics", rh.GetMetrics).Methods("GET") prefixedRouter.HandleFunc("/metrics", rh.GetMetrics).Methods("GET")
} else { } else {
// extended build // extended build
ext.SetupMetricsRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.Log) ext.SetupMetricsRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, AuthHandler(rh.c), rh.c.Log)
ext.SetupSearchRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.RepoDB, rh.c.CveInfo, rh.c.Log) ext.SetupSearchRoutes(rh.c.Config, prefixedRouter, rh.c.StoreController, rh.c.RepoDB, rh.c.CveInfo, rh.c.Log)
gqlPlayground.SetupGQLPlaygroundRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.Log) ext.SetupUIRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.Log)
gqlPlayground.SetupGQLPlaygroundRoutes(rh.c.Config, prefixedRouter, rh.c.StoreController, rh.c.Log)
} }
} }
} }

View file

@ -107,7 +107,8 @@ func TestServeExtensions(t *testing.T) {
WaitTillServerReady(baseURL) WaitTillServerReady(baseURL)
data, err := os.ReadFile(logFile.Name()) data, err := os.ReadFile(logFile.Name())
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(string(data), ShouldContainSubstring, "\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":null") //nolint:lll // gofumpt conflicts with lll So(string(data), ShouldContainSubstring,
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":null,\"UI\":null") //nolint:lll // gofumpt conflicts with lll
}) })
} }
@ -148,7 +149,7 @@ func testWithMetricsEnabled(cfgContentFormat string) {
data, err := os.ReadFile(logFile.Name()) data, err := os.ReadFile(logFile.Name())
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(string(data), ShouldContainSubstring, So(string(data), ShouldContainSubstring,
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":{\"Enable\":true,\"Prometheus\":{\"Path\":\"/metrics\"}},\"Scrub\":null,\"Lint\":null}") //nolint:lll // gofumpt conflicts with lll "\"Metrics\":{\"Enable\":true,\"Prometheus\":{\"Path\":\"/metrics\"}}")
} }
func TestServeMetricsExtension(t *testing.T) { func TestServeMetricsExtension(t *testing.T) {
@ -272,7 +273,7 @@ func TestServeMetricsExtension(t *testing.T) {
data, err := os.ReadFile(logFile.Name()) data, err := os.ReadFile(logFile.Name())
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(string(data), ShouldContainSubstring, So(string(data), ShouldContainSubstring,
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":{\"Enable\":false,\"Prometheus\":{\"Path\":\"/metrics\"}},\"Scrub\":null,\"Lint\":null}}") //nolint:lll // gofumpt conflicts with lll "\"Metrics\":{\"Enable\":false,\"Prometheus\":{\"Path\":\"/metrics\"}}") //nolint:lll // gofumpt conflicts with lll
}) })
} }
@ -472,8 +473,7 @@ func TestServeScrubExtension(t *testing.T) {
defer os.Remove(logPath) // clean up defer os.Remove(logPath) // clean up
// Even if in config we specified scrub interval=1h, the minimum interval is 2h // Even if in config we specified scrub interval=1h, the minimum interval is 2h
dataStr := string(data) dataStr := string(data)
So(dataStr, ShouldContainSubstring, So(dataStr, ShouldContainSubstring, "\"Scrub\":{\"Enable\":true,\"Interval\":3600000000000}")
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":{\"Enable\":true,\"Interval\":3600000000000},\"Lint\":null") //nolint:lll // gofumpt conflicts with lll
So(dataStr, ShouldContainSubstring, So(dataStr, ShouldContainSubstring,
"Scrub interval set to too-short interval < 2h, changing scrub duration to 2 hours and continuing.") "Scrub interval set to too-short interval < 2h, changing scrub duration to 2 hours and continuing.")
}) })
@ -536,8 +536,7 @@ func TestServeScrubExtension(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
defer os.Remove(logPath) // clean up defer os.Remove(logPath) // clean up
dataStr := string(data) dataStr := string(data)
So(dataStr, ShouldContainSubstring, So(dataStr, ShouldContainSubstring, "\"Scrub\":{\"Enable\":false,\"Interval\":86400000000000}")
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":{\"Enable\":false,\"Interval\":86400000000000},\"Lint\":null}") //nolint:lll // gofumpt conflicts with lll
So(dataStr, ShouldContainSubstring, "Scrub config not provided, skipping scrub") So(dataStr, ShouldContainSubstring, "Scrub config not provided, skipping scrub")
So(dataStr, ShouldNotContainSubstring, So(dataStr, ShouldNotContainSubstring,
"Scrub interval set to too-short interval < 2h, changing scrub duration to 2 hours and continuing.") "Scrub interval set to too-short interval < 2h, changing scrub duration to 2 hours and continuing.")
@ -639,7 +638,8 @@ func TestServeSearchEnabled(t *testing.T) {
// to avoid data race when multiple go routines write to trivy DB instance. // to avoid data race when multiple go routines write to trivy DB instance.
defer os.Remove(logPath) // clean up defer os.Remove(logPath) // clean up
substring := "\"Extensions\":{\"Search\":{\"Enable\":true,\"CVE\":null},\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":null}" //nolint:lll // gofumpt conflicts with lll substring := `"Extensions":{"Search":{"Enable":true,"CVE":null}`
found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout) found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout)
So(found, ShouldBeTrue) So(found, ShouldBeTrue)
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -680,7 +680,8 @@ func TestServeSearchEnabledCVE(t *testing.T) {
// to avoid data race when multiple go routines write to trivy DB instance. // to avoid data race when multiple go routines write to trivy DB instance.
WaitTillTrivyDBDownloadStarted(tempDir) WaitTillTrivyDBDownloadStarted(tempDir)
substring := "\"Extensions\":{\"Search\":{\"Enable\":true,\"CVE\":{\"UpdateInterval\":3600000000000,\"Trivy\":null}},\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":null}" //nolint:lll // gofumpt conflicts with lll substring := "\"Search\":{\"Enable\":true,\"CVE\":{\"UpdateInterval\":3600000000000,\"Trivy\":null}}"
found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout) found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout)
So(found, ShouldBeTrue) So(found, ShouldBeTrue)
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -726,7 +727,7 @@ func TestServeSearchEnabledNoCVE(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
defer os.Remove(logPath) // clean up defer os.Remove(logPath) // clean up
substring := "\"Extensions\":{\"Search\":{\"Enable\":true,\"CVE\":null},\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":null}" //nolint:lll // gofumpt conflicts with lll substring := `"Extensions":{"Search":{"Enable":true,"CVE":null}` //nolint:lll // gofumpt conflicts with lll
found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout) found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout)
So(found, ShouldBeTrue) So(found, ShouldBeTrue)
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -768,7 +769,7 @@ func TestServeSearchDisabled(t *testing.T) {
defer os.Remove(logPath) // clean up defer os.Remove(logPath) // clean up
dataStr := string(data) dataStr := string(data)
So(dataStr, ShouldContainSubstring, So(dataStr, ShouldContainSubstring,
"\"Extensions\":{\"Search\":{\"Enable\":false,\"CVE\":{\"UpdateInterval\":10800000000000,\"Trivy\":null}},\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":null}") //nolint:lll // gofumpt conflicts with lll "\"Search\":{\"Enable\":false,\"CVE\":{\"UpdateInterval\":10800000000000,\"Trivy\":null}")
So(dataStr, ShouldContainSubstring, "CVE config not provided, skipping CVE update") So(dataStr, ShouldContainSubstring, "CVE config not provided, skipping CVE update")
So(dataStr, ShouldNotContainSubstring, So(dataStr, ShouldNotContainSubstring,
"CVE update interval set to too-short interval < 2h, changing update duration to 2 hours and continuing.") "CVE update interval set to too-short interval < 2h, changing update duration to 2 hours and continuing.")

View file

@ -33,7 +33,7 @@ func SetupGQLPlaygroundRoutes(conf *config.Config, router *mux.Router,
} }
//nolint:lll //nolint:lll
router.PathPrefix(constants.RoutePrefix + debugCst.GQLPlaygroundEndpoint).HandlerFunc(func(writer http.ResponseWriter, req *http.Request) { router.PathPrefix(debugCst.GQLPlaygroundEndpoint).HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Add("Content-Type", "text/html") writer.Header().Add("Content-Type", "text/html")
proto := "" proto := ""

View file

@ -17,9 +17,12 @@ import (
_ "zotregistry.io/zot/swagger" _ "zotregistry.io/zot/swagger"
) )
func SetupSwaggerRoutes(conf *config.Config, router *mux.Router, log log.Logger, func SetupSwaggerRoutes(conf *config.Config, router *mux.Router, authFunc mux.MiddlewareFunc,
log log.Logger,
) { ) {
log.Info().Msg("setting up swagger route") log.Info().Msg("setting up swagger route")
// swagger swagger "/swagger/v2/index.html" // swagger swagger "/swagger/v2/index.html"
router.PathPrefix("/swagger/v2/").Methods("GET").Handler(httpSwagger.WrapHandler) swgRouter := router.PathPrefix("/swagger/v2/").Subrouter()
swgRouter.Use(authFunc)
swgRouter.Methods("GET").Handler(httpSwagger.WrapHandler)
} }

View file

@ -16,7 +16,8 @@ import (
_ "zotregistry.io/zot/swagger" _ "zotregistry.io/zot/swagger"
) )
func SetupSwaggerRoutes(conf *config.Config, router *mux.Router, log log.Logger, func SetupSwaggerRoutes(conf *config.Config, router *mux.Router, authFunc mux.MiddlewareFunc,
log log.Logger,
) { ) {
// swagger swagger "/swagger/v2/index.html" // swagger swagger "/swagger/v2/index.html"
log.Warn().Msg("skipping enabling swagger because given zot binary " + log.Warn().Msg("skipping enabling swagger because given zot binary " +

View file

@ -17,6 +17,7 @@ type ExtensionConfig struct {
Metrics *MetricsConfig Metrics *MetricsConfig
Scrub *ScrubConfig Scrub *ScrubConfig
Lint *LintConfig Lint *LintConfig
UI *UIConfig
} }
type LintConfig struct { type LintConfig struct {
@ -52,3 +53,7 @@ type ScrubConfig struct {
BaseConfig `mapstructure:",squash"` BaseConfig `mapstructure:",squash"`
Interval time.Duration Interval time.Duration
} }
type UIConfig struct {
BaseConfig `mapstructure:",squash"`
}

View file

@ -0,0 +1,19 @@
//go:build !search || !ui
// +build !search !ui
package extensions
import (
"github.com/gorilla/mux"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
)
func SetupUIRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController,
log log.Logger,
) {
log.Warn().Msg("skipping setting up ui routes because given zot binary doesn't include this feature," +
"please build a binary that does so")
}

View file

@ -0,0 +1,51 @@
//go:build search && ui
// +build search,ui
package extensions
import (
"embed"
"io/fs"
"net/http"
"github.com/gorilla/mux"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
)
// content is our static web server content.
//
//go:embed build/*
var content embed.FS
type uiHandler struct {
log log.Logger
}
func (uih uiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
buf, _ := content.ReadFile("build/index.html")
_, err := w.Write(buf)
if err != nil {
uih.log.Error().Err(err).Msg("unable to serve index.html")
}
}
func SetupUIRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController,
log log.Logger,
) {
if config.Extensions.UI != nil {
fsub, _ := fs.Sub(content, "build")
uih := uiHandler{log: log}
router.PathPrefix("/login").Handler(uih)
router.PathPrefix("/home").Handler(uih)
router.PathPrefix("/explore").Handler(uih)
router.PathPrefix("/image").Handler(uih)
router.PathPrefix("/").Handler(http.FileServer(http.FS(fsub)))
log.Info().Msg("setting up ui routes")
}
}

View file

@ -27,12 +27,13 @@ func EnableMetricsExtension(config *config.Config, log log.Logger, rootDir strin
} }
func SetupMetricsRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController, func SetupMetricsRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController,
log log.Logger, authFunc mux.MiddlewareFunc, log log.Logger,
) { ) {
log.Info().Msg("setting up metrics routes") log.Info().Msg("setting up metrics routes")
if config.Extensions.Metrics != nil && *config.Extensions.Metrics.Enable { if config.Extensions.Metrics != nil && *config.Extensions.Metrics.Enable {
router.PathPrefix(config.Extensions.Metrics.Prometheus.Path). extRouter := router.PathPrefix(config.Extensions.Metrics.Prometheus.Path).Subrouter()
Handler(promhttp.Handler()) extRouter.Use(authFunc)
extRouter.Methods("GET").Handler(promhttp.Handler())
} }
} }

View file

@ -19,7 +19,7 @@ func EnableMetricsExtension(config *config.Config, log log.Logger, rootDir strin
// SetupMetricsRoutes ... // SetupMetricsRoutes ...
func SetupMetricsRoutes(conf *config.Config, router *mux.Router, func SetupMetricsRoutes(conf *config.Config, router *mux.Router,
storeController storage.StoreController, log log.Logger, storeController storage.StoreController, authFunc mux.MiddlewareFunc, log log.Logger,
) { ) {
log.Warn().Msg("skipping setting up metrics routes because given zot binary doesn't include this feature," + log.Warn().Msg("skipping setting up metrics routes because given zot binary doesn't include this feature," +
"please build a binary that does so") "please build a binary that does so")

View file

@ -84,8 +84,9 @@ func SetupSearchRoutes(config *config.Config, router *mux.Router, storeControlle
if config.Extensions.Search != nil && *config.Extensions.Search.Enable { if config.Extensions.Search != nil && *config.Extensions.Search.Enable {
resConfig := search.GetResolverConfig(log, storeController, repoDB, cveInfo) resConfig := search.GetResolverConfig(log, storeController, repoDB, cveInfo)
graphqlPrefix := router.PathPrefix(constants.FullSearchPrefix).Methods("OPTIONS", "GET", "POST") extRouter := router.PathPrefix(constants.ExtSearchPrefix).Subrouter()
graphqlPrefix.Handler(gqlHandler.NewDefaultServer(gql_generated.NewExecutableSchema(resConfig))) extRouter.Methods("GET", "POST", "OPTIONS").
Handler(gqlHandler.NewDefaultServer(gql_generated.NewExecutableSchema(resConfig)))
} }
} }