mirror of
https://github.com/Quiq/docker-registry-ui.git
synced 2025-09-30 14:57:26 +00:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7a3bc551b3 | ||
|
7a752d3f8d | ||
|
80bdad8c91 | ||
|
734afe56b5 | ||
|
0f6bf65015 | ||
|
7525e87c1a | ||
|
1b8c502a60 |
@@ -1,5 +1,12 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### 0.7.3 (2018-08-14)
|
||||||
|
|
||||||
|
* Add `registry_password_file` option to the config file.
|
||||||
|
* Improve no data message on empty tables on UI.
|
||||||
|
* Show the root namespace "library" in the dropdown even when there are no repos in it.
|
||||||
|
* Switch alpine Docker image to 3.8.
|
||||||
|
|
||||||
### 0.7.2 (2018-07-30)
|
### 0.7.2 (2018-07-30)
|
||||||
|
|
||||||
* Make web root accessible w/o trailing slash when base_path is configured.
|
* Make web root accessible w/o trailing slash when base_path is configured.
|
||||||
|
@@ -18,7 +18,7 @@ RUN cd /opt/src/github.com/quiq/docker-registry-ui && \
|
|||||||
go build -o /opt/docker-registry-ui github.com/quiq/docker-registry-ui
|
go build -o /opt/docker-registry-ui github.com/quiq/docker-registry-ui
|
||||||
|
|
||||||
|
|
||||||
FROM alpine:3.7
|
FROM alpine:3.8
|
||||||
|
|
||||||
WORKDIR /opt
|
WORKDIR /opt
|
||||||
RUN apk add --no-cache ca-certificates && \
|
RUN apk add --no-cache ca-certificates && \
|
||||||
|
@@ -12,8 +12,11 @@ verify_tls: true
|
|||||||
# They need to have a full access to the registry.
|
# They need to have a full access to the registry.
|
||||||
# If token authentication service is enabled, it will be auto-discovered and those credentials
|
# If token authentication service is enabled, it will be auto-discovered and those credentials
|
||||||
# will be used to obtain access tokens.
|
# will be used to obtain access tokens.
|
||||||
|
# When the registry_password_file entry is used, the password can be passed as a docker secret
|
||||||
|
# and read from file. This overides the registry_password entry.
|
||||||
registry_username: user
|
registry_username: user
|
||||||
registry_password: pass
|
registry_password: pass
|
||||||
|
# registry_password_file: /run/secrets/registry_password_file
|
||||||
|
|
||||||
# Event listener token.
|
# Event listener token.
|
||||||
# The same one should be configured on Docker registry as Authorization Bearer token.
|
# The same one should be configured on Docker registry as Authorization Bearer token.
|
||||||
|
12
main.go
12
main.go
@@ -26,6 +26,7 @@ type configData struct {
|
|||||||
VerifyTLS bool `yaml:"verify_tls"`
|
VerifyTLS bool `yaml:"verify_tls"`
|
||||||
Username string `yaml:"registry_username"`
|
Username string `yaml:"registry_username"`
|
||||||
Password string `yaml:"registry_password"`
|
Password string `yaml:"registry_password"`
|
||||||
|
PasswordFile string `yaml:"registry_password_file"`
|
||||||
EventListenerToken string `yaml:"event_listener_token"`
|
EventListenerToken string `yaml:"event_listener_token"`
|
||||||
EventRetentionDays int `yaml:"event_retention_days"`
|
EventRetentionDays int `yaml:"event_retention_days"`
|
||||||
EventDatabaseDriver string `yaml:"event_database_driver"`
|
EventDatabaseDriver string `yaml:"event_database_driver"`
|
||||||
@@ -86,6 +87,17 @@ func main() {
|
|||||||
a.config.BasePath = a.config.BasePath[0 : len(a.config.BasePath)-1]
|
a.config.BasePath = a.config.BasePath[0 : len(a.config.BasePath)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Read password from file.
|
||||||
|
if a.config.PasswordFile != "" {
|
||||||
|
if _, err := os.Stat(a.config.PasswordFile); os.IsNotExist(err) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
passwordBytes, err := ioutil.ReadFile(a.config.PasswordFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
a.config.Password = strings.TrimSuffix(string(passwordBytes[:]), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
// Init registry API client.
|
// Init registry API client.
|
||||||
a.client = registry.NewClient(a.config.RegistryURL, a.config.VerifyTLS, a.config.Username, a.config.Password)
|
a.client = registry.NewClient(a.config.RegistryURL, a.config.VerifyTLS, a.config.Username, a.config.Password)
|
||||||
|
@@ -148,6 +148,9 @@ func (c *Client) Namespaces() []string {
|
|||||||
for k := range c.repos {
|
for k := range c.repos {
|
||||||
namespaces = append(namespaces, k)
|
namespaces = append(namespaces, k)
|
||||||
}
|
}
|
||||||
|
if !ItemInSlice("library", namespaces) {
|
||||||
|
namespaces = append(namespaces, "library")
|
||||||
|
}
|
||||||
sort.Strings(namespaces)
|
sort.Strings(namespaces)
|
||||||
return namespaces
|
return namespaces
|
||||||
}
|
}
|
||||||
|
@@ -42,3 +42,13 @@ func PrettySize(size float64) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%.*f %s", 0, size, units[i])
|
return fmt.Sprintf("%.*f %s", 0, size, units[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ItemInSlice check if item is an element of slice
|
||||||
|
func ItemInSlice(item string, slice []string) bool {
|
||||||
|
for _, i := range slice {
|
||||||
|
if i == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@@ -45,3 +45,13 @@ func TestPrettySize(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestItemInSlice(t *testing.T) {
|
||||||
|
a := []string{"abc", "def", "ghi"}
|
||||||
|
convey.Convey("Check whether element is in slice", t, func() {
|
||||||
|
convey.So(ItemInSlice("abc", a), convey.ShouldBeTrue)
|
||||||
|
convey.So(ItemInSlice("ghi", a), convey.ShouldBeTrue)
|
||||||
|
convey.So(ItemInSlice("abc1", a), convey.ShouldBeFalse)
|
||||||
|
convey.So(ItemInSlice("gh", a), convey.ShouldBeFalse)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -39,6 +39,7 @@ func setupRenderer(debug bool, registryHost, basePath string) *Template {
|
|||||||
view := jet.NewHTMLSet("templates")
|
view := jet.NewHTMLSet("templates")
|
||||||
view.SetDevelopmentMode(debug)
|
view.SetDevelopmentMode(debug)
|
||||||
|
|
||||||
|
view.AddGlobal("version", version)
|
||||||
view.AddGlobal("basePath", basePath)
|
view.AddGlobal("basePath", basePath)
|
||||||
view.AddGlobal("registryHost", registryHost)
|
view.AddGlobal("registryHost", registryHost)
|
||||||
view.AddGlobal("pretty_size", func(size interface{}) string {
|
view.AddGlobal("pretty_size", func(size interface{}) string {
|
||||||
|
@@ -22,8 +22,8 @@
|
|||||||
{{yield body()}}
|
{{yield body()}}
|
||||||
|
|
||||||
<div style="padding: 10px 0; margin-bottom: 20px">
|
<div style="padding: 10px 0; margin-bottom: 20px">
|
||||||
<div style="float: left">
|
<div style="text-align: center; color:darkgrey">
|
||||||
© 2017-2018 <a href="https://goquiq.com">Quiq Inc.</a>
|
Docker Registry UI v{{version}} © 2017-2018 <a href="https://goquiq.com">Quiq Inc.</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -6,7 +6,10 @@
|
|||||||
$('#datatable').DataTable({
|
$('#datatable').DataTable({
|
||||||
"pageLength": 10,
|
"pageLength": 10,
|
||||||
"order": [[ 4, 'desc' ]],
|
"order": [[ 4, 'desc' ]],
|
||||||
"stateSave": true
|
"stateSave": true,
|
||||||
|
"language": {
|
||||||
|
"emptyTable": "No events."
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -3,10 +3,6 @@
|
|||||||
{{block head()}}
|
{{block head()}}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#datatable').DataTable({
|
|
||||||
"pageLength": 25,
|
|
||||||
"stateSave": true
|
|
||||||
});
|
|
||||||
$('#namespace').on('change', function (e) {
|
$('#namespace').on('change', function (e) {
|
||||||
window.location = '{{ basePath }}/' + this.value;
|
window.location = '{{ basePath }}/' + this.value;
|
||||||
});
|
});
|
||||||
@@ -18,6 +14,14 @@
|
|||||||
namespace = namespace.split('/')[1]
|
namespace = namespace.split('/')[1]
|
||||||
}
|
}
|
||||||
$('#namespace').val(namespace);
|
$('#namespace').val(namespace);
|
||||||
|
|
||||||
|
$('#datatable').DataTable({
|
||||||
|
"pageLength": 25,
|
||||||
|
"stateSave": true,
|
||||||
|
"language": {
|
||||||
|
"emptyTable": "No repositories in \"" + namespace + "\" namespace."
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@@ -11,7 +11,10 @@
|
|||||||
"stateSave": true,
|
"stateSave": true,
|
||||||
columnDefs: [
|
columnDefs: [
|
||||||
{ type: 'natural', targets: 0 }
|
{ type: 'natural', targets: 0 }
|
||||||
]
|
],
|
||||||
|
"language": {
|
||||||
|
"emptyTable": "No tags in this repository."
|
||||||
|
}
|
||||||
})
|
})
|
||||||
function populateConfirmation() {
|
function populateConfirmation() {
|
||||||
$('[data-toggle=confirmation]').confirmation({
|
$('[data-toggle=confirmation]').confirmation({
|
||||||
|
3
version.go
Normal file
3
version.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
const version = "0.7.3"
|
Reference in New Issue
Block a user