From eabcf4849480531de563f98568ef7ee884d22807 Mon Sep 17 00:00:00 2001 From: Yuhi Ishikura Date: Thu, 7 Jun 2018 21:46:53 +0900 Subject: [PATCH 1/2] Add example. --- example/.gitignore | 1 + example/README.md | 25 ++++++++++++++++++++++ example/config.yml | 44 ++++++++++++++++++++++++++++++++++++++ example/docker-compose.yml | 24 +++++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 example/.gitignore create mode 100644 example/README.md create mode 100644 example/config.yml create mode 100644 example/docker-compose.yml diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..249cda9 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1 @@ +/data \ No newline at end of file diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..d175ea6 --- /dev/null +++ b/example/README.md @@ -0,0 +1,25 @@ +# Docker Registry UI Example + +Start the local Docker Registry and UI. + +```bash +$ docker-compose up -d +``` + +As an example, push the docker-registry-ui image to the local Docker Registry. + +```bash +$ docker tag quiq/docker-registry-ui localhost:5000/quiq/docker-registry-ui +$ docker push localhost:5000/quiq/docker-registry-ui +The push refers to repository [localhost:5000/quiq/docker-registry-ui] +ab414a599bf8: Pushed +a8da33adf86e: Pushed +71a0e0a972a7: Pushed +96dc74eb5456: Pushed +ac362bf380d0: Pushed +04a094fe844e: Pushed +latest: digest: sha256:d88c1ca40986a358e59795992e87e364a0b3b97833aade5abcd79dda0a0477e8 size: 1571 +``` + +Then you will find the pushed repository 'quiq/docker-registry-ui' in the following URL. +http://localhost:8000/quiq/docker-registry-ui diff --git a/example/config.yml b/example/config.yml new file mode 100644 index 0000000..f65f591 --- /dev/null +++ b/example/config.yml @@ -0,0 +1,44 @@ +# Listen interface. +listen_addr: 0.0.0.0:8000 + +# Registry URL with schema and port. +registry_url: http://registry:5000 +# Verify TLS certificate when using https. +verify_tls: true + +# Docker registry credentials. +# They need to have a full access to the registry. +# If token authentication service is enabled, it will be auto-discovered and those credentials +# will be used to obtain access tokens. +# registry_username: user +# registry_password: pass + +# Event listener token. +# The same one should be configured on Docker registry as Authorization Bearer token. +event_listener_token: token +# Retention of records to keep. +event_retention_days: 7 + +# Event listener storage. +event_database_driver: sqlite3 +event_database_location: data/registry_events.db +# event_database_driver: mysql +# event_database_location: user:password@tcp(localhost:3306)/docker_events + +# Cache refresh interval in minutes. +# How long to cache repository list and tag counts. +cache_refresh_interval: 10 + +# If users can delete tags. If set to False, then only admins listed below. +anyone_can_delete: false +# Users allowed to delete tags. +# This should be sent via X-WEBAUTH-USER header from your proxy. +admins: [] + +# Debug mode. Affects only templates. +debug: true + +# CLI options. +# How many days to keep tags but also keep the minimal count provided no matter how old. +purge_tags_keep_days: 90 +purge_tags_keep_count: 2 diff --git a/example/docker-compose.yml b/example/docker-compose.yml new file mode 100644 index 0000000..dd80fd8 --- /dev/null +++ b/example/docker-compose.yml @@ -0,0 +1,24 @@ +version: "2.3" + +services: + registry: + image: registry:2 + ports: + - "5000:5000" + volumes: + - "./data/registry:/var/lib/registry" + healthcheck: + test: ["CMD", "wget", "-s", "localhost:5000/v2/"] + interval: 5s + timeout: 10s + + registry-ui: + image: quiq/docker-registry-ui:latest + ports: + - "8000:8000" + volumes: + - "./data/registry-ui:/opt/data" + - "./config.yml:/opt/config.yml:ro" + depends_on: + registry: + condition: service_healthy From a5fc3a5d30b13361a9f575f951f7260a412b4fc2 Mon Sep 17 00:00:00 2001 From: Yuhi Ishikura Date: Thu, 7 Jun 2018 22:36:40 +0900 Subject: [PATCH 2/2] Add feature to change the base path. --- config.yml | 2 + example/README.md | 6 +- example/config/httpd.conf | 79 +++++++++++++++++++ .../{config.yml => config/registry-ui.yml} | 2 + example/docker-compose.yml | 13 ++- main.go | 30 ++++--- templates/base.html | 2 +- templates/event_log.html | 2 +- templates/repositories.html | 12 +-- templates/tag_info.html | 6 +- templates/tags.html | 10 +-- 11 files changed, 133 insertions(+), 31 deletions(-) create mode 100644 example/config/httpd.conf rename example/{config.yml => config/registry-ui.yml} (96%) diff --git a/config.yml b/config.yml index 4b68325..39e51a1 100644 --- a/config.yml +++ b/config.yml @@ -1,5 +1,7 @@ # Listen interface. listen_addr: 0.0.0.0:8000 +# Base path of Docker Registry UI. +base_path: / # Registry URL with schema and port. registry_url: https://docker-registry.local diff --git a/example/README.md b/example/README.md index d175ea6..b221eec 100644 --- a/example/README.md +++ b/example/README.md @@ -9,8 +9,8 @@ $ docker-compose up -d As an example, push the docker-registry-ui image to the local Docker Registry. ```bash -$ docker tag quiq/docker-registry-ui localhost:5000/quiq/docker-registry-ui -$ docker push localhost:5000/quiq/docker-registry-ui +$ docker tag quiq/docker-registry-ui localhost/quiq/docker-registry-ui +$ docker push localhost/quiq/docker-registry-ui The push refers to repository [localhost:5000/quiq/docker-registry-ui] ab414a599bf8: Pushed a8da33adf86e: Pushed @@ -22,4 +22,4 @@ latest: digest: sha256:d88c1ca40986a358e59795992e87e364a0b3b97833aade5abcd79dda0 ``` Then you will find the pushed repository 'quiq/docker-registry-ui' in the following URL. -http://localhost:8000/quiq/docker-registry-ui +http://localhost/ui/quiq/docker-registry-ui diff --git a/example/config/httpd.conf b/example/config/httpd.conf new file mode 100644 index 0000000..05d9e9c --- /dev/null +++ b/example/config/httpd.conf @@ -0,0 +1,79 @@ +LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule headers_module modules/mod_headers.so + +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule access_compat_module modules/mod_access_compat.so + +LoadModule log_config_module modules/mod_log_config.so + +LoadModule proxy_module modules/mod_proxy.so +LoadModule proxy_http_module modules/mod_proxy_http.so + +LoadModule unixd_module modules/mod_unixd.so + + + User daemon + Group daemon + + +ServerAdmin you@example.com + +ErrorLog /proc/self/fd/2 + +LogLevel warn + + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + CustomLog /proc/self/fd/1 common + + +ServerRoot "/usr/local/apache2" + +Listen 80 + + + AllowOverride none + Require all denied + + + + + ServerName myregistrydomain.com + + Header always set "Docker-Distribution-Api-Version" "registry/2.0" + Header onsuccess set "Docker-Distribution-Api-Version" "registry/2.0" + + ProxyRequests off + ProxyPreserveHost on + + # no proxy for /error/ (Apache HTTPd errors messages) + ProxyPass /error/ ! + + ProxyPass /v2 http://registry:5000/v2 + ProxyPassReverse /v2 http://registry:5000/v2 + + + Order deny,allow + Allow from all + + + ProxyPass /ui/ http://registry-ui:8000/ui/ + ProxyPassReverse /ui/ http://registry-ui:8000/ui/ + + + Order deny,allow + Allow from all + + + \ No newline at end of file diff --git a/example/config.yml b/example/config/registry-ui.yml similarity index 96% rename from example/config.yml rename to example/config/registry-ui.yml index f65f591..2cb042a 100644 --- a/example/config.yml +++ b/example/config/registry-ui.yml @@ -1,5 +1,7 @@ # Listen interface. listen_addr: 0.0.0.0:8000 +# Base path of Docker Registry UI. +base_path: /ui # Registry URL with schema and port. registry_url: http://registry:5000 diff --git a/example/docker-compose.yml b/example/docker-compose.yml index dd80fd8..74c1987 100644 --- a/example/docker-compose.yml +++ b/example/docker-compose.yml @@ -1,10 +1,17 @@ version: "2.3" services: + httpd: + image: httpd:2.4 + ports: + - "80:80" + volumes: + - "./config/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro" + registry: image: registry:2 ports: - - "5000:5000" + - "5000" volumes: - "./data/registry:/var/lib/registry" healthcheck: @@ -15,10 +22,10 @@ services: registry-ui: image: quiq/docker-registry-ui:latest ports: - - "8000:8000" + - "8000" volumes: - "./data/registry-ui:/opt/data" - - "./config.yml:/opt/config.yml:ro" + - "./config/registry-ui.yml:/opt/config.yml:ro" depends_on: registry: condition: service_healthy diff --git a/main.go b/main.go index 145b84b..9321ab6 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( type configData struct { ListenAddr string `yaml:"listen_addr"` + BasePath string `yaml:"base_path"` RegistryURL string `yaml:"registry_url"` VerifyTLS bool `yaml:"verify_tls"` Username string `yaml:"registry_username"` @@ -75,6 +76,15 @@ func main() { if err != nil { panic(err) } + // Normalize base path. + if a.config.BasePath != "" { + if !strings.HasPrefix(a.config.BasePath, "/") { + a.config.BasePath = "/" + a.config.BasePath + } + if strings.HasSuffix(a.config.BasePath, "/") { + a.config.BasePath = a.config.BasePath[0 : len(a.config.BasePath)-1] + } + } // Init registry API client. a.client = registry.NewClient(a.config.RegistryURL, a.config.VerifyTLS, a.config.Username, a.config.Password) @@ -99,6 +109,7 @@ func main() { // Template engine init. view := jet.NewHTMLSet("templates") view.SetDevelopmentMode(a.config.Debug) + view.AddGlobal("base_path", a.config.BasePath) view.AddGlobal("registryHost", u.Host) view.AddGlobal("pretty_size", func(size interface{}) string { var value float64 @@ -126,22 +137,22 @@ func main() { e.Renderer = &template{View: view} // Web routes. - e.Static("/static", "static") - e.GET("/", a.viewRepositories) - e.GET("/:namespace", a.viewRepositories) - e.GET("/:namespace/:repo", a.viewTags) - e.GET("/:namespace/:repo/:tag", a.viewTagInfo) - e.GET("/:namespace/:repo/:tag/delete", a.deleteTag) - e.GET("/events", a.viewLog) + e.Static(a.config.BasePath+"/static", "static") + e.GET(a.config.BasePath+"/", a.viewRepositories) + e.GET(a.config.BasePath+"/:namespace", a.viewRepositories) + e.GET(a.config.BasePath+"/:namespace/:repo", a.viewTags) + e.GET(a.config.BasePath+"/:namespace/:repo/:tag", a.viewTagInfo) + e.GET(a.config.BasePath+"/:namespace/:repo/:tag/delete", a.deleteTag) + e.GET(a.config.BasePath+"/events", a.viewLog) // Protected event listener. - p := e.Group("/api") + p := e.Group(a.config.BasePath + "/api") p.Use(middleware.KeyAuthWithConfig(middleware.KeyAuthConfig{ Validator: middleware.KeyAuthValidator(func(token string, c echo.Context) (bool, error) { return token == a.config.EventListenerToken, nil }), })) - p.POST("/events", a.receiveEvents) + p.POST(a.config.BasePath+"/events", a.receiveEvents) e.Logger.Fatal(e.Start(a.config.ListenAddr)) } @@ -170,7 +181,6 @@ func (a *apiClient) viewRepositories(c echo.Context) error { } repos, _ := a.client.Repositories(true)[namespace] - data := jet.VarMap{} data.Set("namespace", namespace) data.Set("namespaces", a.client.Namespaces()) diff --git a/templates/base.html b/templates/base.html index 579fa3d..5a0abfd 100644 --- a/templates/base.html +++ b/templates/base.html @@ -15,7 +15,7 @@

Docker Registry UI

- Event Log + Event Log
diff --git a/templates/event_log.html b/templates/event_log.html index 29a485f..a62fc1a 100644 --- a/templates/event_log.html +++ b/templates/event_log.html @@ -14,7 +14,7 @@ {{block body()}} diff --git a/templates/repositories.html b/templates/repositories.html index d2f998d..d4a8d43 100644 --- a/templates/repositories.html +++ b/templates/repositories.html @@ -8,12 +8,14 @@ "stateSave": true }); $('#namespace').on('change', function (e) { - window.location = '/' + this.value; + window.location = '{{ base_path }}/' + this.value; }); - if (window.location.pathname == '/') { + namespace = window.location.pathname; + namespace = namespace.replace("{{ base_path }}", ""); + if (namespace == '/') { namespace = 'library'; } else { - namespace = window.location.pathname.split('/')[1] + namespace = namespace.split('/')[1] } $('#namespace').val(namespace); }); @@ -31,7 +33,7 @@ @@ -44,7 +46,7 @@ {{range repo := repos}} - + {{end}} diff --git a/templates/tag_info.html b/templates/tag_info.html index e9b7cfa..581293c 100644 --- a/templates/tag_info.html +++ b/templates/tag_info.html @@ -4,11 +4,11 @@ {{block body()}}
{{ repo }}{{ repo }} {{ tagCounts[namespace+"/"+repo] }}
diff --git a/templates/tags.html b/templates/tags.html index c185578..ca65be3 100644 --- a/templates/tags.html +++ b/templates/tags.html @@ -1,7 +1,7 @@ {{extends "base.html"}} {{block head()}} - +