Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple static registries #219

Merged
merged 5 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ This web user interface uses [Riot](https://github.com/Riot/riot) the react-like
- Show/Hide content digest in taglist via `SHOW_CONTENT_DIGEST` (values are: [`true`, `false`], default: `false`) (see [#126](https://github.com/Joxit/docker-registry-ui/issues/126)).
- Limit the number of elements in the image list via `CATALOG_ELEMENTS_LIMIT` (see [#127](https://github.com/Joxit/docker-registry-ui/pull/127)).
- Multi arch support in history page (see [#130](https://github.com/Joxit/docker-registry-ui/issues/130) and [#134](https://github.com/Joxit/docker-registry-ui/pull/134))
- Set a list of default registries with `DEFAULT_REGISTRIES` (see [#219](https://github.com/Joxit/docker-registry-ui/pull/219)).
- Desactivate add and remove regisitries with `READ_ONLY_REGISTRIES` (see [#219](https://github.com/Joxit/docker-registry-ui/pull/219)).

## FAQ

Expand Down Expand Up @@ -85,9 +87,11 @@ Some env options are available for use this interface for **only one server**.
- `DELETE_IMAGES`: Set if we can delete images from the UI. (default: `false`)
- `SHOW_CONTENT_DIGEST`: Show content digest in docker tag list. (default: `false`)
- `CATALOG_ELEMENTS_LIMIT`: Limit the number of elements in the catalog page. (default: `100000`).
- `SINGLE_REGISTRY`: Remove the menu that show the dialogs to add, remove and change the endpoint of your docker registry. (default `false`)
- `SINGLE_REGISTRY`: Remove the menu that show the dialogs to add, remove and change the endpoint of your docker registry. (default: `false`).
- `NGINX_PROXY_PASS_URL`: Update the default Nginx configuration and set the **proxy_pass** to your backend docker registry (this avoid CORS configuration). This is usually the name of your registry container in the form `http://registry:5000`.
- `NGINX_PROXY_HEADER_*`: Update the default Nginx configuration and set **custom headers** for your backend docker registry. Only when `NGINX_PROXY_PASS_URL` is used.
- `DEFAULT_REGISTRIES`: List of comma separated registry URLs (e.g `http://registry.example.com,http://registry:5000`), available only when `SINGLE_REGISTRY=false`. (default: ` `).
- `READ_ONLY_REGISTRIES`: Desactivate dialog for remove and add new registries, available only when `SINGLE_REGISTRY=false`. (default: `false`).

There are some examples with [docker-compose](https://docs.docker.com/compose/) and docker-registry-ui as proxy [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-proxy/) or docker-registry-ui as standalone [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-standalone/).

Expand Down Expand Up @@ -182,3 +186,4 @@ check out the [Electron](examples/electron/README.md) standalone application.
- [UI showing same sha256 content digest for all tags + Delete is not working](https://github.com/Joxit/docker-registry-ui/tree/main/examples/issue-116) ([#116](https://github.com/Joxit/docker-registry-ui/issues/116))
- [Electron-based Standalone Application](https://github.com/Joxit/docker-registry-ui/tree/main/examples/electron) ([#129](https://github.com/Joxit/docker-registry-ui/pull/129))
- [Use docker-registry-ui as proxy with read-only right](https://github.com/Joxit/docker-registry-ui/tree/main/examples/read-only-auth) ([#47](https://github.com/Joxit/docker-registry-ui/issues/47))
- [Use DEFAULT_REGISTRIES and READ_ONLY_REGISTRIES](https://github.com/Joxit/docker-registry-ui/tree/main/examples/pr-219) ([#219](https://github.com/Joxit/docker-registry-ui/issues/219))
14 changes: 8 additions & 6 deletions bin/entrypoint
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#!/bin/sh

sed -i "s,\${REGISTRY_URL},${REGISTRY_URL}," index.html
sed -i "s,\${REGISTRY_TITLE},${REGISTRY_TITLE}," index.html
sed -i "s,\${PULL_URL},${PULL_URL}," index.html
sed -i "s,\${SINGLE_REGISTRY},${SINGLE_REGISTRY}," index.html
sed -i "s/\${CATALOG_ELEMENTS_LIMIT}/${CATALOG_ELEMENTS_LIMIT}/" index.html
sed -i "s/\${SHOW_CONTENT_DIGEST}/${SHOW_CONTENT_DIGEST}/" index.html
sed -i "s~\${REGISTRY_URL}~${REGISTRY_URL}~" index.html
sed -i "s~\${REGISTRY_TITLE}~${REGISTRY_TITLE}~" index.html
sed -i "s~\${PULL_URL}~${PULL_URL}~" index.html
sed -i "s~\${SINGLE_REGISTRY}~${SINGLE_REGISTRY}~" index.html
sed -i "s~\${CATALOG_ELEMENTS_LIMIT}~${CATALOG_ELEMENTS_LIMIT}~" index.html
sed -i "s~\${SHOW_CONTENT_DIGEST}~${SHOW_CONTENT_DIGEST}~" index.html
sed -i "s~\${DEFAULT_REGISTRIES}~${DEFAULT_REGISTRIES}~" index.html
sed -i "s~\${READ_ONLY_REGISTRIES}~${READ_ONLY_REGISTRIES}~" index.html

if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
sed -i "s/\${DELETE_IMAGES}/false/" index.html
Expand Down
3 changes: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
- [Add custom headers bases on environment variable and/or file when the ui is used as proxy](https://github.com/Joxit/docker-registry-ui/tree/main/examples/proxy-headers) ([#89](https://github.com/Joxit/docker-registry-ui/pull/89))
- [UI showing same sha256 content digest for all tags + Delete is not working](https://github.com/Joxit/docker-registry-ui/tree/main/examples/issue-116) ([#116](https://github.com/Joxit/docker-registry-ui/issues/116))
- [Electron-based Standalone Application](https://github.com/Joxit/docker-registry-ui/tree/main/examples/electron) ([#129](https://github.com/Joxit/docker-registry-ui/pull/129))
- [Use docker-registry-ui as proxy with read-only right](https://github.com/Joxit/docker-registry-ui/tree/main/examples/read-only-auth) ([#47](https://github.com/Joxit/docker-registry-ui/issues/47))
- [Use docker-registry-ui as proxy with read-only right](https://github.com/Joxit/docker-registry-ui/tree/main/examples/read-only-auth) ([#47](https://github.com/Joxit/docker-registry-ui/issues/47))
- [Use DEFAULT_REGISTRIES and READ_ONLY_REGISTRIES](https://github.com/Joxit/docker-registry-ui/tree/main/examples/pr-219) ([#219](https://github.com/Joxit/docker-registry-ui/issues/219))
14 changes: 14 additions & 0 deletions examples/pr-219/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Example for pull request #219

Basic usage for `DEFAULT_REGISTRIES` and `READ_ONLY_REGISTRIES`.

Behaviors:
- `DEFAULT_REGISTRIES`:
- will set the list of registries in the localstorage when the localstorage is empty.
- will overwrite the list of registries every time when `READ_ONLY_REGISTRIES=true`
- `READ_ONLY_REGISTRIES`:
- will remove dialog for Add and Remove registries

These options works only when `SINGLE_REGISTRY=false`

See [#219](https://github.com/Joxit/docker-registry-ui/pull/219)
39 changes: 39 additions & 0 deletions examples/pr-219/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
version: '2'

services:
registry_1:
image: registry:latest
restart: always
ports:
- 5000:5000
container_name: registry_1
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: "['*']"
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
volumes:
- ./data:/var/lib/registry

registry_2:
image: registry:latest
restart: always
ports:
- 5001:5000
container_name: registry_2
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: "['*']"
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
volumes:
- ./data:/var/lib/registry

ui:
image: joxit/docker-registry-ui:latest
restart: always
container_name: registry-ui
environment:
- REGISTRY_TITLE=Private Docker Registry
- DEFAULT_REGISTRIES=http://localhost:5000,http://localhost:5001
- DELETE_IMAGES=true
- READ_ONLY_REGISTRIES=true
- SINGLE_REGISTRY=false
ports:
- 80:80
20 changes: 15 additions & 5 deletions src/components/catalog/catalog.riot
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div if="{ !state.loadend }" class="spinner-wrapper">
<material-spinner></material-spinner>
</div>
<catalog-element each="{ item in state.repositories }" item="{ item }" filter-results="{ props.filterResults }"/>
<catalog-element each="{ item in state.repositories }" item="{ item }" filter-results="{ props.filterResults }" />
<script>
import CatalogElement from './catalog-element.riot'
import {
Http
} from '../../scripts/http';
import {
getRegistryServers
} from '../../scripts/utils';

export default {
components: {
Expand All @@ -41,18 +44,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
registryName: '',
length: 0,
loadend: false,
repositories: []
repositories: [],
registryUrl: ''
},

onBeforeMount(props) {
this.state.registryName = props.registryName;
this.state.catalogElementsLimit = props.catalogElementsLimit;
},
onMounted(props) {
this.display(props, this.state)
onMounted(props, state) {
this.display(props, state)
},
onUpdated(props, state) {
this.display(props, state);
},

display(props, state) {
if (props.registryUrl === state.registryUrl) {
return;
}
state.registryUrl = props.registryUrl;
let repositories = [];
const self = this;
const oReq = new Http({
Expand Down
6 changes: 2 additions & 4 deletions src/components/dialogs/add-registry-url.riot
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</material-popup>
<script>
import {
getRegistryServers
addRegistryServers
} from '../../scripts/utils';
import router from '../../scripts/router';

Expand All @@ -51,9 +51,7 @@
if (!input.value.startsWith('http')) {
return this.props.onNotify('The input field should start with http:// or https://.', true);
}
const url = input.value.trim().replace(/\/*$/, '');
const registryServer = getRegistryServers().filter(e => e !== url);
localStorage.setItem('registryServer', JSON.stringify([url].concat(registryServer)));
const url = addRegistryServers(input.value);
router.home()
this.props.onServerChange(url);
this.props.onClose()
Expand Down
5 changes: 2 additions & 3 deletions src/components/dialogs/change-registry-url.riot
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
</material-popup>
<script>
import {
addRegistryServers,
getRegistryServers
} from '../../scripts/utils';
import router from '../../scripts/router';
Expand All @@ -45,9 +46,7 @@
if (!select.value.startsWith('http')) {
return this.props.onNotify('The select field should start with http:// or https://.', true);
}
const url = select.value.trim().replace(/\/*$/, '');
const registryServer = getRegistryServers().filter(e => e !== url);
localStorage.setItem('registryServer', JSON.stringify([url].concat(registryServer)));
const url = addRegistryServers(select.value);
router.home()
this.props.onServerChange(url);
this.props.onClose()
Expand Down
15 changes: 9 additions & 6 deletions src/components/dialogs/dialogs-menu.riot
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<dialogs-menu>
<add-registry-url opened="{ state['add-registry-url'] }" on-close="{ onClose('add-registry-url') }"
<add-registry-url if="{ !props.readOnlyRegistries }" opened="{ state['add-registry-url'] }" on-close="{ onClose('add-registry-url') }"
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></add-registry-url>
<change-registry-url opened="{ state['change-registry-url'] }" on-close="{ onClose('change-registry-url') }"
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></change-registry-url>
<remove-registry-url opened="{ state['remove-registry-url'] }" on-close="{ onClose('remove-registry-url') }"
<remove-registry-url if="{ !props.readOnlyRegistries }" opened="{ state['remove-registry-url'] }" on-close="{ onClose('remove-registry-url') }"
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></remove-registry-url>
<div class="container">
<material-button onClick="{ onClick }" waves-center="true" rounded="true" waves-opacity="0.6" waves-duration="600">
<i class="material-icons">more_vert</i>
</material-button>
<material-dropdown-list items="{ dropdownItems }" onSelect="{ onDropdownSelect }"
<material-dropdown-list items="{ dropdownItems.filter(item => item.ro || !props.readOnlyRegistries) }" onSelect="{ onDropdownSelect }"
opened="{ state.isDropdownOpened }" />
</div>
<div class="overlay" onclick="{ onClick }" if="{ state.isDropdownOpened }"></div>
Expand All @@ -42,13 +42,16 @@
},
dropdownItems: [{
title: 'Add URL',
name: 'add-registry-url'
name: 'add-registry-url',
ro: false
}, {
title: 'Change URL',
name: 'change-registry-url'
name: 'change-registry-url',
ro: true
}, {
title: 'Remove URL',
name: 'remove-registry-url'
name: 'remove-registry-url',
ro: false
}],
onDropdownSelect(key, item) {
this.update({
Expand Down
6 changes: 3 additions & 3 deletions src/components/dialogs/remove-registry-url.riot
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</material-popup>
<script>
import {
getRegistryServers
getRegistryServers,
removeRegistryServers
} from '../../scripts/utils';
export default {
remove(event) {
const url = event.currentTarget.attributes.url && event.currentTarget.attributes.url.value;
const registryServer = getRegistryServers().filter(e => e !== url);
localStorage.setItem('registryServer', JSON.stringify(registryServer));
removeRegistryServers(url);
setTimeout(() => this.update(), 100);
},
getRegistryServers
Expand Down
9 changes: 8 additions & 1 deletion src/components/docker-registry-ui.riot
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="logo">Docker Registry UI</div>
<search-bar on-search="{ onSearch }"></search-bar>
<dialogs-menu if="{props.singleRegistry !== 'true'}" on-notify="{ notifySnackbar }"
on-server-change="{ onServerChange }"></dialogs-menu>
on-server-change="{ onServerChange }" default-registries="{ props.defaultRegistries }"
read-only-registries="{ truthy(props.readOnlyRegistries) }"></dialogs-menu>
</material-navbar>
</header>
<main>
Expand Down Expand Up @@ -77,6 +78,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import {
stripHttps,
getRegistryServers,
setRegistryServers,
truthy
} from '../scripts/utils';
import router from '../scripts/router';
Expand All @@ -96,6 +98,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
state.snackbarMessage = undefined;
},
onBeforeMount(props) {
if ((props.defaultRegistries && props.defaultRegistries.length > 0 && getRegistryServers().length === 0) ||
truthy(props.readOnlyRegistries)) {
setRegistryServers(props.defaultRegistries);
}

// props.singleRegistry === 'true' means old static version
const registryUrl = props.registryUrl ||
(props.singleRegistry === 'true' ? undefined : (router.getUrlQueryParam() || getRegistryServers(0))) ||
Expand Down
3 changes: 2 additions & 1 deletion src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
<!-- build:keep production -->
<docker-registry-ui registry-url="${REGISTRY_URL}" name="${REGISTRY_TITLE}" pull-url="${PULL_URL}"
show-content-digest="${SHOW_CONTENT_DIGEST}" is-image-remove-activated="${DELETE_IMAGES}"
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}" single-registry="${SINGLE_REGISTRY}">
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}" single-registry="${SINGLE_REGISTRY}"
default-registries="${DEFAULT_REGISTRIES}" read-only-registries="${READ_ONLY_REGISTRIES}">
</docker-registry-ui>
<!-- endbuild -->
<!-- build:keep developement -->
Expand Down
30 changes: 27 additions & 3 deletions src/scripts/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const LOCAL_STORAGE_KEY = 'registryServer';

export function bytesToSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == undefined || isNaN(bytes)) {
Expand Down Expand Up @@ -152,14 +154,36 @@ export const ERROR_CAN_NOT_READ_CONTENT_DIGEST = {

export function getRegistryServers(i) {
try {
const res = JSON.parse(localStorage.getItem('registryServer'));
const res = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY));
if (res instanceof Array) {
return !isNaN(i) ? res[i] : res.map((url) => url.trim().replace(/\/*$/, ''));
}
} catch (e) {}
return !isNaN(i) ? '' : [];
}

export function setRegistryServers(registries) {
if (typeof registries === 'string') {
registries = registries.split(',');
} else if (!Array.isArray(registries)) {
throw new Error('setRegistries must be called with string or array parameter');
}
registries = registries.map((registry) => registry.replace(/\/*$/, ''));
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(registries));
}

export function addRegistryServers(registry) {
const url = registry.trim().replace(/\/*$/, '');
const registryServer = getRegistryServers().filter((e) => e !== url);
setRegistryServers([url].concat(registryServer));
return url;
}

export function removeRegistryServers(registry) {
const registryServers = getRegistryServers().filter((e) => e !== registry);
setRegistryServers(registryServers);
}

export function encodeURI(url) {
if (!url) {
return;
Expand All @@ -175,5 +199,5 @@ export function decodeURI(url) {
}

export function truthy(value) {
return value === true || value === "true";
}
return value === true || value === 'true';
}