diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 55eae48d..5f907d67 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -106,7 +106,7 @@ jobs: make OS=$OS ARCH=$ARCH sudo env "PATH=$PATH" make privileged-test else - make OS=$OS ARCH=$ARCH binary binary-minimal binary-debug binary-ui cli bench exporter-minimal + make OS=$OS ARCH=$ARCH binary binary-minimal binary-debug cli bench exporter-minimal fi env: S3MOCK_ENDPOINT: localhost:4566 @@ -142,6 +142,10 @@ jobs: if: github.event_name == 'release' && github.event.action== 'published' name: Push OCI images to GitHub Packages runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + packages: write strategy: matrix: os: [linux, darwin] @@ -204,7 +208,7 @@ jobs: - name: Build and push zot-minimal container image uses: project-stacker/stacker-build-push-action@main with: - file: 'build/stacker.yaml' + file: 'build/stacker-minimal.yaml' build-args: | RELEASE_TAG=${{ github.event.release.tag_name }} COMMIT=${{ github.event.release.tag_name }}-${{ github.sha }} diff --git a/Makefile b/Makefile index 21e90948..e1293a9b 100644 --- a/Makefile +++ b/Makefile @@ -21,20 +21,20 @@ REGCLIENT := $(TOOLSDIR)/bin/regctl REGCLIENT_VERSION := v0.4.5 ACTION_VALIDATOR := $(TOOLSDIR)/bin/action-validator ACTION_VALIDATOR_VERSION := v0.2.1 -ZUI_VERSION := v2.0.0-rc2 +ZUI_VERSION := v2.0.0-rc3 STACKER := $(TOOLSDIR)/bin/stacker BATS := $(TOOLSDIR)/bin/bats TESTDATA := $(TOP_LEVEL)/test/data OS ?= linux ARCH ?= amd64 BENCH_OUTPUT ?= stdout -EXTENSIONS ?= sync,search,scrub,metrics,lint +EXTENSIONS ?= sync,search,scrub,metrics,lint,ui comma:= , hyphen:= - extended-name:= .PHONY: all -all: modcheck swagger binary binary-minimal binary-debug binary-ui cli bench exporter-minimal verify-config test covhtml check check-gh-actions +all: modcheck swagger binary binary-minimal binary-debug cli bench exporter-minimal verify-config test covhtml check check-gh-actions .PHONY: modcheck modcheck: @@ -68,10 +68,6 @@ binary-debug: $(if $(findstring ui,$(EXTENSIONS)), ui) 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 -.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 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 @@ -166,7 +162,7 @@ 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 $(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 $(EXTENSIONS),containers_image_openpgp,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,$(EXTENSIONS),containers_image_openpgp ./... $(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags stress,$(EXTENSIONS),containers_image_openpgp ./... diff --git a/build/stacker-minimal.yaml b/build/stacker-minimal.yaml new file mode 100644 index 00000000..279d23bf --- /dev/null +++ b/build/stacker-minimal.yaml @@ -0,0 +1,62 @@ +build: + from: + type: docker + url: docker://ghcr.io/project-zot/golang:1.19 + binds: + - ../. -> /zotcopy + run: | + export GO111MODULE=on + export GOPATH='/go' + export HOME='/root' + export PATH='/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' + mkdir -p /go/src/github.com/project-zot + cd /go/src/github.com/project-zot + git clone /zotcopy zot + cd /go/src/github.com/project-zot/zot + make COMMIT=${{COMMIT}} OS=${{OS}} ARCH=${{ARCH}} RELEASE_TAG=${{RELEASE_TAG}} clean binary${{EXT:}} + cat > config.json << EOF + { + "storage":{ + "rootDirectory":"/var/lib/registry" + }, + "http":{ + "address":"0.0.0.0", + "port":"5000" + }, + "log":{ + "level":"debug" + } + } + EOF + + cat config.json + mkdir -p /zotcopy/.build/${{REPO_NAME}} + cd /zotcopy/.build/${{REPO_NAME}} + + mkdir -p binary/ cert/ config/ + + cp /go/src/github.com/project-zot/zot/bin/zot-${{OS}}-${{ARCH}}${{EXT:}} binary/ + cp /go/src/github.com/project-zot/zot/config.json config/ + cp /etc/ssl/certs/ca-certificates.crt cert/ + build_only: true + +"${{REPO_NAME:zot}}": + os: ${{OS}} + arch: ${{ARCH}} + from: + type: docker + url: docker://gcr.io/distroless/base:latest-${{ARCH}} + overlay_dirs: + - source: ../.build/${{REPO_NAME}}/binary + dest: /usr/local/bin + - source: ../.build/${{REPO_NAME}}/cert + dest: /etc/ssl/certs + - source: ../.build/${{REPO_NAME}}/config + dest: /etc/zot + entrypoint: + - /usr/local/bin/zot-${{OS}}-${{ARCH}}${{EXT:}} + volumes: + - /var/lib/registry + cmd: + - serve + - /etc/zot/config.json diff --git a/build/stacker.yaml b/build/stacker.yaml index 65a26ef3..6a10ed8a 100644 --- a/build/stacker.yaml +++ b/build/stacker.yaml @@ -25,6 +25,17 @@ build: }, "log":{ "level":"debug" + }, + "extensions": { + "search": { + "enable": true, + "cve": { + "updateInterval": "2h" + } + }, + "ui": { + "enable": true + } } } EOF @@ -45,7 +56,7 @@ build: arch: ${{ARCH}} from: type: docker - url: docker://zothub.io/c3/base:jammy + url: docker://gcr.io/distroless/base:latest-${{ARCH}} overlay_dirs: - source: ../.build/${{REPO_NAME}}/binary dest: /usr/local/bin diff --git a/pkg/extensions/extension-ui.go b/pkg/extensions/extension_ui.go similarity index 100% rename from pkg/extensions/extension-ui.go rename to pkg/extensions/extension_ui.go diff --git a/pkg/extensions/extension-ui-disabled.go b/pkg/extensions/extension_ui_disabled.go similarity index 100% rename from pkg/extensions/extension-ui-disabled.go rename to pkg/extensions/extension_ui_disabled.go diff --git a/pkg/extensions/extension_ui_test.go b/pkg/extensions/extension_ui_test.go new file mode 100644 index 00000000..82df5c84 --- /dev/null +++ b/pkg/extensions/extension_ui_test.go @@ -0,0 +1,106 @@ +//go:build search && ui +// +build search,ui + +package extensions_test + +import ( + "io" + "net/http" + "os" + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" + "gopkg.in/resty.v1" + + "zotregistry.io/zot/pkg/api" + "zotregistry.io/zot/pkg/api/config" + extconf "zotregistry.io/zot/pkg/extensions/config" + "zotregistry.io/zot/pkg/test" +) + +func TestUIExtension(t *testing.T) { + Convey("Verify zot with UI extension starts successfully", t, func() { + conf := config.New() + port := test.GetFreePort() + baseURL := test.GetBaseURL(port) + conf.HTTP.Port = port + + // we won't use the logging config feature as we want logs in both + // stdout and a file + logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt") + So(err, ShouldBeNil) + logPath := logFile.Name() + defer os.Remove(logPath) + + writers := io.MultiWriter(os.Stdout, logFile) + + defaultValue := true + + conf.Extensions = &extconf.ExtensionConfig{} + conf.Extensions.UI = &extconf.UIConfig{ + BaseConfig: extconf.BaseConfig{Enable: &defaultValue}, + } + conf.Storage.RootDirectory = t.TempDir() + + ctlr := api.NewController(conf) + ctlr.Log.Logger = ctlr.Log.Output(writers) + + ctlrManager := test.NewControllerManager(ctlr) + ctlrManager.StartAndWait(port) + + found, err := test.ReadLogFileAndSearchString(logPath, "\"UI\":{\"Enable\":true}", 2*time.Minute) + So(found, ShouldBeTrue) + So(err, ShouldBeNil) + + found, err = test.ReadLogFileAndSearchString(logPath, "setting up ui routes", 2*time.Minute) + So(found, ShouldBeTrue) + So(err, ShouldBeNil) + + cfg, layers, manifest, err := test.GetImageComponents(1) + So(err, ShouldBeNil) + + repoName := "test-repo" + tagName := "test-tag" + + // Upload a test image + err = test.UploadImage( + test.Image{ + Config: cfg, + Layers: layers, + Manifest: manifest, + Tag: tagName, + }, baseURL, repoName) + So(err, ShouldBeNil) + + resp, err := resty.R().Get(baseURL + "/home") + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + So(resp.StatusCode(), ShouldEqual, http.StatusOK) + + resp, err = resty.R().Get(baseURL + "/image/") + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + So(resp.StatusCode(), ShouldEqual, http.StatusOK) + + resp, err = resty.R().Get(baseURL + "/image/" + repoName) + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + So(resp.StatusCode(), ShouldEqual, http.StatusOK) + + resp, err = resty.R().Get(baseURL + "/image/" + repoName + "/tag/") + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + So(resp.StatusCode(), ShouldEqual, http.StatusOK) + + resp, err = resty.R().Get(baseURL + "/image/" + repoName + "/tag/" + tagName) + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + So(resp.StatusCode(), ShouldEqual, http.StatusOK) + + resp, err = resty.R().Get(baseURL + "/badurl/") + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + So(resp.StatusCode(), ShouldEqual, http.StatusNotFound) + }) +}