mirror of
https://github.com/project-zot/zot.git
synced 2025-01-06 22:40:28 -05:00
ui: initial commit
Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com> (cherry picked from commit d557da0baba819b7cd7e6b5941528776e125ac6d)
This commit is contained in:
parent
f3faae0e09
commit
97f1fc0f98
10 changed files with 134 additions and 16 deletions
18
Makefile
18
Makefile
|
@ -45,7 +45,7 @@ build-metadata:
|
||||||
go list -tags $(EXTENSIONS) -f '{{ join .GoFiles "\n" }}' ./... | sort -u
|
go list -tags $(EXTENSIONS) -f '{{ join .GoFiles "\n" }}' ./... | sort -u
|
||||||
|
|
||||||
.PHONY: binary
|
.PHONY: binary
|
||||||
binary: modcheck swagger create-name build-metadata
|
binary: modcheck swagger create-name build-metadata ui
|
||||||
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.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.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
|
||||||
|
@ -134,7 +134,7 @@ $(GOLINTER):
|
||||||
$(GOLINTER) version
|
$(GOLINTER) version
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: ./golangcilint.yaml $(GOLINTER)
|
check: ./golangcilint.yaml $(GOLINTER) ui
|
||||||
$(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 dev,containers_image_openpgp ./...
|
$(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags dev,containers_image_openpgp ./...
|
||||||
|
@ -187,6 +187,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
|
||||||
|
@ -323,3 +324,16 @@ fuzz-all:
|
||||||
.PHONY: anonymous-push-pull
|
.PHONY: anonymous-push-pull
|
||||||
anonymous-push-pull: binary check-skopeo $(BATS)
|
anonymous-push-pull: binary check-skopeo $(BATS)
|
||||||
$(BATS) --trace --print-output-on-failure test/blackbox/anonymous_policiy.bats
|
$(BATS) --trace --print-output-on-failure test/blackbox/anonymous_policiy.bats
|
||||||
|
|
||||||
|
.PHONY: ui
|
||||||
|
ui:
|
||||||
|
pwd=$$(pwd);\
|
||||||
|
tdir=$$(mktemp -d);\
|
||||||
|
cd $$tdir;\
|
||||||
|
git clone https://github.com/project-zot/zui.git;\
|
||||||
|
cd zui;\
|
||||||
|
npm install;\
|
||||||
|
npm run build;\
|
||||||
|
cd $$pwd;\
|
||||||
|
rm -rf ./pkg/extensions/build;\
|
||||||
|
cp -R $$tdir/zui/build ./pkg/extensions/;
|
||||||
|
|
23
examples/config-ui.json
Normal file
23
examples/config-ui.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"distSpecVersion": "1.0.1-dev",
|
||||||
|
"storage": {
|
||||||
|
"rootDirectory": "/tmp/zot"
|
||||||
|
},
|
||||||
|
"http": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port": "8080"
|
||||||
|
},
|
||||||
|
"log": {
|
||||||
|
"level": "debug"
|
||||||
|
},
|
||||||
|
"extensions": {
|
||||||
|
"search": {
|
||||||
|
"cve": {
|
||||||
|
"updateInterval": "2h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ui": {
|
||||||
|
"enable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,7 +58,8 @@ func allowedMethods(method string) []string {
|
||||||
|
|
||||||
// nolint: contextcheck
|
// nolint: contextcheck
|
||||||
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", NameRegexp.String()),
|
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", NameRegexp.String()),
|
||||||
rh.ListTags).Methods(allowedMethods("GET")...)
|
rh.ListTags).Methods(allowedMethods("GET")...)
|
||||||
|
@ -113,7 +113,10 @@ func (rh *RouteHandler) SetupRoutes() {
|
||||||
constants.ArtifactSpecRoutePrefix, NameRegexp.String()), rh.GetReferrers).Methods("GET")
|
constants.ArtifactSpecRoutePrefix, NameRegexp.String()), rh.GetReferrers).Methods("GET")
|
||||||
|
|
||||||
// swagger swagger "/swagger/v2/index.html"
|
// swagger swagger "/swagger/v2/index.html"
|
||||||
rh.c.Router.PathPrefix("/swagger/v2/").Methods("GET").Handler(httpSwagger.WrapHandler)
|
swgRouter := rh.c.Router.PathPrefix("/swagger/v2/").Subrouter()
|
||||||
|
swgRouter.Use(AuthHandler(rh.c))
|
||||||
|
swgRouter.Methods("GET").Handler(httpSwagger.WrapHandler)
|
||||||
|
|
||||||
// Setup Extensions Routes
|
// Setup Extensions Routes
|
||||||
if rh.c.Config != nil {
|
if rh.c.Config != nil {
|
||||||
if rh.c.Config.Extensions == nil {
|
if rh.c.Config.Extensions == nil {
|
||||||
|
@ -121,8 +124,9 @@ 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.Log)
|
ext.SetupSearchRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, AuthHandler(rh.c), rh.c.Log)
|
||||||
|
ext.SetupUIRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.Log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ type ExtensionConfig struct {
|
||||||
Metrics *MetricsConfig
|
Metrics *MetricsConfig
|
||||||
Scrub *ScrubConfig
|
Scrub *ScrubConfig
|
||||||
Lint *LintConfig
|
Lint *LintConfig
|
||||||
|
UI *UIConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type LintConfig struct {
|
type LintConfig struct {
|
||||||
|
@ -41,3 +42,7 @@ type PrometheusConfig struct {
|
||||||
type ScrubConfig struct {
|
type ScrubConfig struct {
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UIConfig struct {
|
||||||
|
Enable *bool
|
||||||
|
}
|
||||||
|
|
18
pkg/extensions/extension-ui-disabled.go
Normal file
18
pkg/extensions/extension-ui-disabled.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
//go:build !search && !ui_base
|
||||||
|
// +build !search,!ui_base
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
52
pkg/extensions/extension-ui.go
Normal file
52
pkg/extensions/extension-ui.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
//go:build search || ui_base
|
||||||
|
// +build search ui_base
|
||||||
|
|
||||||
|
package extensions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"zotregistry.io/zot/pkg/log"
|
||||||
|
"zotregistry.io/zot/pkg/storage"
|
||||||
|
|
||||||
|
"zotregistry.io/zot/pkg/api/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// content is our static web server content.
|
||||||
|
//go:embed build/*
|
||||||
|
var content embed.FS
|
||||||
|
|
||||||
|
func SetupUIRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController,
|
||||||
|
l log.Logger,
|
||||||
|
) {
|
||||||
|
// fork a new zerolog child to avoid data race
|
||||||
|
log := log.Logger{Logger: l.With().Caller().Timestamp().Logger()}
|
||||||
|
|
||||||
|
if config.Extensions.UI != nil {
|
||||||
|
fsub, _ := fs.Sub(content, "build")
|
||||||
|
|
||||||
|
router.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
buf, _ := content.ReadFile("build/index.html")
|
||||||
|
|
||||||
|
_, err := w.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("unable to serve index.html")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.HandleFunc("/home", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
buf, _ := content.ReadFile("build/index.html")
|
||||||
|
|
||||||
|
_, err := w.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("unable to serve index.html")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.PathPrefix("/").Handler(http.FileServer(http.FS(fsub)))
|
||||||
|
log.Info().Msg("setting up ui routes")
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,14 +26,14 @@ 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,
|
||||||
l log.Logger,
|
authFunc mux.MiddlewareFunc, l log.Logger,
|
||||||
) {
|
) {
|
||||||
// fork a new zerolog child to avoid data race
|
// fork a new zerolog child to avoid data race
|
||||||
log := log.Logger{Logger: l.With().Caller().Timestamp().Logger()}
|
log := log.Logger{Logger: l.With().Caller().Timestamp().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).Handler(promhttp.Handler()).Subrouter()
|
||||||
Handler(promhttp.Handler())
|
extRouter.Use(authFunc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,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")
|
||||||
|
|
|
@ -56,7 +56,7 @@ func downloadTrivyDB(dbDir string, log log.Logger, updateInterval time.Duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupSearchRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController,
|
func SetupSearchRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController,
|
||||||
l log.Logger,
|
authFunc mux.MiddlewareFunc, l log.Logger,
|
||||||
) {
|
) {
|
||||||
// fork a new zerolog child to avoid data race
|
// fork a new zerolog child to avoid data race
|
||||||
log := log.Logger{Logger: l.With().Caller().Timestamp().Logger()}
|
log := log.Logger{Logger: l.With().Caller().Timestamp().Logger()}
|
||||||
|
@ -71,8 +71,10 @@ func SetupSearchRoutes(config *config.Config, router *mux.Router, storeControlle
|
||||||
resConfig = search.GetResolverConfig(log, storeController, false)
|
resConfig = search.GetResolverConfig(log, storeController, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
graphqlPrefix := router.PathPrefix(constants.ExtSearchPrefix).Methods("OPTIONS", "GET", "POST")
|
extRouter := router.PathPrefix(constants.ExtSearchPrefix).Subrouter()
|
||||||
graphqlPrefix.Handler(gqlHandler.NewDefaultServer(gql_generated.NewExecutableSchema(resConfig)))
|
extRouter.Use(authFunc)
|
||||||
|
extRouter.Methods("GET", "POST", "OPTIONS").
|
||||||
|
Handler(gqlHandler.NewDefaultServer(gql_generated.NewExecutableSchema(resConfig)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ func EnableSearchExtension(config *config.Config, log log.Logger, rootDir string
|
||||||
|
|
||||||
// SetupSearchRoutes ...
|
// SetupSearchRoutes ...
|
||||||
func SetupSearchRoutes(conf *config.Config, router *mux.Router,
|
func SetupSearchRoutes(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 search routes because given zot binary doesn't include this feature," +
|
log.Warn().Msg("skipping setting up search routes because given zot binary doesn't include this feature," +
|
||||||
"please build a binary that does so")
|
"please build a binary that does so")
|
||||||
|
|
Loading…
Reference in a new issue