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:
parent
d12836e69c
commit
c0aaca8ed1
16 changed files with 189 additions and 37 deletions
15
.github/workflows/ci-cd.yml
vendored
15
.github/workflows/ci-cd.yml
vendored
|
@ -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
3
.gitignore
vendored
|
@ -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/
|
||||||
|
|
44
Makefile
44
Makefile
|
@ -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/;
|
||||||
|
|
|
@ -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
23
examples/config-ui.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.")
|
||||||
|
|
|
@ -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 := ""
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 " +
|
||||||
|
|
|
@ -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"`
|
||||||
|
}
|
||||||
|
|
19
pkg/extensions/extension-ui-disabled.go
Normal file
19
pkg/extensions/extension-ui-disabled.go
Normal 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")
|
||||||
|
}
|
51
pkg/extensions/extension-ui.go
Normal file
51
pkg/extensions/extension-ui.go
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue