Merge pull request #8 from uphy/master

Add feature to change the base path
This commit is contained in:
Roman Vynar 2018-07-04 16:47:31 +03:00 committed by GitHub
commit 51c9a66195
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 221 additions and 25 deletions

View File

@ -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

1
example/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/data

25
example/README.md Normal file
View File

@ -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/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
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/ui/quiq/docker-registry-ui

79
example/config/httpd.conf Normal file
View File

@ -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
<IfModule unixd_module>
User daemon
Group daemon
</IfModule>
ServerAdmin you@example.com
ErrorLog /proc/self/fd/2
LogLevel warn
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
CustomLog /proc/self/fd/1 common
</IfModule>
ServerRoot "/usr/local/apache2"
Listen 80
<Directory />
AllowOverride none
Require all denied
</Directory>
<VirtualHost *:80>
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
<Location /v2>
Order deny,allow
Allow from all
</Location>
ProxyPass /ui/ http://registry-ui:8000/ui/
ProxyPassReverse /ui/ http://registry-ui:8000/ui/
<Location /ui>
Order deny,allow
Allow from all
</Location>
</VirtualHost>

View File

@ -0,0 +1,46 @@
# 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
# 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

View File

@ -0,0 +1,31 @@
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"
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"
volumes:
- "./data/registry-ui:/opt/data"
- "./config/registry-ui.yml:/opt/config.yml:ro"
depends_on:
registry:
condition: service_healthy

30
main.go
View File

@ -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
@ -137,22 +148,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))
}
@ -181,7 +192,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())

View File

@ -15,7 +15,7 @@
<h2>Docker Registry UI</h2>
</div>
<div style="float: right">
<a href="/events">Event Log</a>
<a href="{{ base_path }}/events">Event Log</a>
</div>
<div style="clear: both"></div>

View File

@ -14,7 +14,7 @@
{{block body()}}
<ol class="breadcrumb">
<li><a href="/">Home</a></li>
<li><a href="{{ base_path }}/">Home</a></li>
<li class="active">Event Log</li>
</ol>

View File

@ -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);
});
@ -35,7 +37,7 @@
</div>
<ol class="breadcrumb">
<li><a href="/">Home</a></li>
<li><a href="{{ base_path }}/">Home</a></li>
</ol>
<table id="datatable" class="table table-striped table-bordered">
@ -48,7 +50,7 @@
<tbody>
{{range repo := repos}}
<tr>
<td><a href="/{{ namespace }}/{{ repo |url_encoded_path }}">{{ repo }}</a></td>
<td><a href="{{ base_path }}/{{ namespace }}/{{ repo|url_encoded_path }}">{{ repo }}</a></td>
<td>{{ tagCounts[namespace+"/"+repo] }}</td>
</tr>
{{end}}

View File

@ -4,11 +4,11 @@
{{block body()}}
<ol class="breadcrumb">
<li><a href="/">Home</a></li>
<li><a href="{{ base_path }}/">Home</a></li>
{{if namespace != "library"}}
<li><a href="/{{ namespace }}">{{ namespace }}</a></li>
<li><a href="{{ base_path }}/{{ namespace }}">{{ namespace }}</a></li>
{{end}}
<li><a href="/{{ namespace }}/{{ repo }}">{{ repo |url_decoded_path }}</a></li>
<li><a href="{{ base_path }}/{{ namespace }}/{{ repo|url_decoded_path }}">{{ repo }}</a></li>
<li class="active">{{ tag }}</li>
</ol>
<table class="table table-striped table-bordered">

View File

@ -1,7 +1,7 @@
{{extends "base.html"}}
{{block head()}}
<script type="text/javascript" src="/static/bootstrap-confirmation.min.js"></script>
<script type="text/javascript" src="{{ base_path }}/static/bootstrap-confirmation.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/plug-ins/1.10.16/sorting/natural.js"></script>
<script type="text/javascript">
$(document).ready(function() {
@ -29,9 +29,9 @@
{{block body()}}
<ol class="breadcrumb">
<li><a href="/">Home</a></li>
<li><a href="{{ base_path }}/">Home</a></li>
{{if namespace != "library"}}
<li><a href="/{{ namespace }}">{{ namespace }}</a></li>
<li><a href="{{ base_path }}/{{ namespace }}">{{ namespace }}</a></li>
{{end}}
<li class="active">{{ repo |url_decoded_path }}</li>
</ol>
@ -46,9 +46,9 @@
{{range tag := tags}}
<tr>
<td>
<a href="/{{ namespace }}/{{ repo }}/{{ tag }}">{{ tag }}</a>
<a href="{{ base_path }}/{{ namespace }}/{{ repo }}/{{ tag }}">{{ tag }}</a>
{{if deleteAllowed}}
<a href="/{{ namespace }}/{{ repo }}/{{ tag }}/delete" data-toggle="confirmation" class="btn btn-danger btn-xs pull-right" role="button">Delete</a>
<a href="{{ base_path }}/{{ namespace }}/{{ repo }}/{{ tag }}/delete" data-toggle="confirmation" class="btn btn-danger btn-xs pull-right" role="button">Delete</a>
{{end}}
</td>
</tr>