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

Expose some custom labels #240

Merged
merged 1 commit into from
Mar 26, 2022
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Some env options are available for use this interface for **only one server**.
- `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`).
- `SHOW_CATALOG_NB_TAGS`: Show number of tags per images on catalog page. This will produce + nb images requests, not recommended on large registries. (default: `false`).
- `HISTORY_CUSTOM_LABELS`: Expose custom labels in history page, custom labels will be processed like maintainer label.

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
3 changes: 2 additions & 1 deletion bin/entrypoint
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ 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
sed -i "s~\${SHOW_CATALOG_NB_TAGS}~${SHOW_CATALOG_NB_TAGS}~" index.html
sed -i "s~\${HISTORY_CUSTOM_LABELS}~${HISTORY_CUSTOM_LABELS}~" index.html

if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
sed -i "s/\${DELETE_IMAGES}/false/" index.html
Expand Down Expand Up @@ -64,4 +65,4 @@ if [ "$(whoami)" != "root" ]; then
sed -i "s,/var/run/nginx.pid,/tmp/nginx.pid," /etc/nginx/nginx.conf
fi

sed -i "s,listen 80;,listen $NGINX_LISTEN_PORT;," /etc/nginx/conf.d/default.conf
sed -i "s,listen 80;,listen $NGINX_LISTEN_PORT;," /etc/nginx/conf.d/default.conf
8 changes: 5 additions & 3 deletions src/components/docker-registry-ui.riot
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<tag-history registry-url="{ state.registryUrl }" registry-name="{ state.name }" pull-url="{ state.pullUrl }"
image="{ router.getTagHistoryImage() }" tag="{ router.getTagHistoryTag() }"
is-image-remove-activated="{ truthy(props.isImageRemoveActivated) }" on-notify="{ notifySnackbar }"
on-authentication="{ onAuthentication }"></tag-history>
on-authentication="{ onAuthentication }" history-custom-labels="{ stringToArray(props.historyCustomLabels) }"></tag-history>
</route>
</router>
<registry-authentication realm="{ state.realm }" scope="{ state.scope }" service="{ state.service }"
Expand Down Expand Up @@ -80,7 +80,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
stripHttps,
getRegistryServers,
setRegistryServers,
truthy
truthy,
stringToArray
} from '../scripts/utils';
import router from '../scripts/router';

Expand Down Expand Up @@ -175,7 +176,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
baseRoute: '([^#]*?)/(\\?[^#]*?)?(#!)?(/?)',
router,
version,
truthy
truthy,
stringToArray
}
</script>
</docker-registry-ui>
28 changes: 15 additions & 13 deletions src/components/tag-history/tag-history-element.riot
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<tag-history-element class="{ state.key }">
<div class="headline"><i class="material-icons">{ state.icon }</i>
<div class="headline">
<i class="material-icons">{ state.icon }</i>
<p>{ state.name }</p>
</div>
<div class="content">
<div class="value" if="{ state.value }"> { state.value }</div>
<div class="values value" each="{ value in state.values }" if="{ state.values }"> { value }</div>
<div class="value" if="{ state.value }">{ state.value }</div>
<div class="values value" each="{ value in state.values }" if="{ state.values }">{ value }</div>
</div>
<script>
import {
getHistoryIcon
} from '../../scripts/utils';
import { getHistoryIcon } from '../../scripts/utils';
export default {
onBeforeStart(props, state) {
state.key = props.entry.key;
Expand All @@ -48,14 +47,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
return name;
} else if (name === 'os') {
return 'OS';
} else if (name.startsWith('custom-label-')) {
name = name.replace('custom-label-', '');
}
return name.replace(/([a-z])([A-Z])/g, '$1 $2')
.replace('_', ' ')
return name
.replace(/([a-z])([A-Z])/g, '$1 $2')
.replace(/[_-]/g, ' ')
.split(' ')
.map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
.join(' ');
}
}
},
};
</script>
<style>
:host.Labels .value,
Expand All @@ -70,7 +72,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.

:host.docker_version .headline .material-icons {
background-size: 24px auto;
background-image: url("images/docker-logo.svg");
background-image: url('images/docker-logo.svg');
background-repeat: no-repeat;
}

Expand Down Expand Up @@ -103,4 +105,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
font-size: 12px;
}
</style>
</tag-history-element>
</tag-history-element>
129 changes: 77 additions & 52 deletions src/components/tag-history/tag-history.riot
Original file line number Diff line number Diff line change
Expand Up @@ -20,60 +20,60 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<material-button waves-center="true" rounded="true" waves-color="#ddd" onClick="{ toTaglist }">
<i class="material-icons">arrow_back</i>
</material-button>
<h2>
History of { props.image }:{ props.tag } <i class="material-icons">history</i>
</h2>
<h2>History of { props.image }:{ props.tag } <i class="material-icons">history</i></h2>
</div>
</material-card>
<div if="{ !state.loadend }" class="spinner-wrapper">
<material-spinner />
<material-spinner></material-spinner>
</div>

<material-tabs if="{ state.archs && state.loadend }" useLine="{ true }" tabs="{ state.archs }"
onTabChanged="{ onTabChanged }" />
<material-tabs
if="{ state.archs && state.loadend }"
useLine="{ true }"
tabs="{ state.archs }"
onTabChanged="{ onTabChanged }"
></material-tabs>

<material-card each="{ element in state.elements }" class="tag-history-element">
<tag-history-element each="{ entry in element }" if="{ entry.value && entry.value.length > 0}" entry="{ entry }" />
<tag-history-element
each="{ entry in element }"
if="{ entry.value && entry.value.length > 0}"
entry="{ entry }"
></tag-history-element>
</material-card>
<script>
import {
DockerImage
} from '../../scripts/docker-image';
import {
bytesToSize
} from '../../scripts/utils';
import { DockerImage } from '../../scripts/docker-image';
import { bytesToSize } from '../../scripts/utils';
import router from '../../scripts/router';
import TagHistoryElement from './tag-history-element.riot'
import TagHistoryElement from './tag-history-element.riot';
export default {
components: {
TagHistoryElement
TagHistoryElement,
},
onBeforeMount(props, state) {
state.elements = [];
state.image = new DockerImage(props.image, props.tag, {
list: true,
registryUrl: props.registryUrl,
onNotify: props.onNotify,
onAuthentication: props.onAuthentication
onAuthentication: props.onAuthentication,
});
state.image.fillInfo()
state.image.fillInfo();
},
onMounted(props, state) {
state.image.on('blobs', this.processBlobs);
state.image.on('list', this.multiArchList);
},
onTabChanged(arch, idx) {
const state = this.state;
const {
registryUrl,
onNotify
} = this.props;
state.elements = []
state.image.variants[idx] = state.image.variants[idx] ||
const { registryUrl, onNotify } = this.props;
state.elements = [];
state.image.variants[idx] =
state.image.variants[idx] ||
new DockerImage(this.props.image, arch.digest, {
list: false,
registryUrl,
onNotify
onNotify,
});
if (state.image.variants[idx].blobs) {
return this.processBlobs(state.image.variants[idx].blobs);
Expand All @@ -83,48 +83,52 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
},
processBlobs(blobs) {
const state = this.state;
const { historyCustomLabels } = this.props;

function exec(elt) {
const guiElements = [];
for (var attribute in elt) {
if (elt.hasOwnProperty(attribute) && attribute != 'empty_layer') {
const value = elt[attribute];
const guiElement = {
"key": attribute,
"value": modifySpecificAttributeTypes(attribute, value)
'key': attribute,
'value': modifySpecificAttributeTypes(attribute, value),
};
guiElements.push(guiElement);
}
}
return guiElements.sort(eltSort);
}
const elements = new Array(blobs.history.length + 1);
elements[0] = exec(getConfig(blobs));
elements[0] = exec(getConfig(blobs, { historyCustomLabels }));
blobs.history.forEach(function (elt, i) {
elements[blobs.history.length - i] = exec(elt)
elements[blobs.history.length - i] = exec(elt);
});
this.update({
elements,
loadend: true
loadend: true,
});
},
multiArchList(manifests) {
manifests = manifests.manifests || manifests;
const archs = manifests.map(function (manifest) {
return {
title: manifest.platform.os + '/' + manifest.platform.architecture + (manifest.platform.variant ?
manifest.platform.variant : ''),
digest: manifest.digest
}
title:
manifest.platform.os +
'/' +
manifest.platform.architecture +
(manifest.platform.variant ? manifest.platform.variant : ''),
digest: manifest.digest,
};
});
this.update({
archs
archs,
});
},
toTaglist() {
router.taglist(this.props.image);
}
}
},
};
const eltIdx = function (e) {
switch (e) {
case 'created':
Expand Down Expand Up @@ -158,7 +162,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
return new Date(value).toLocaleString();
case 'created_by':
const cmd = value.match(/\/bin\/sh *-c *#\(nop\) *([A-Z]+)/);
return (cmd && cmd[1]) || 'RUN'
return (cmd && cmd[1]) || 'RUN';
case 'size':
return bytesToSize(value);
case 'Entrypoint':
Expand All @@ -175,26 +179,47 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
return value || '';
};

const getConfig = function (blobs) {
const res = ['architecture', 'User', 'created', 'docker_version', 'os', 'Cmd', 'Entrypoint', 'Env', 'Labels',
'User', 'Volumes', 'WorkingDir', 'author', 'id', 'ExposedPorts'
]
.reduce(function (acc, e) {
const value = blobs[e] || blobs.config[e];
if (value && e === 'architecture' && blobs.variant) {
acc[e] = value + blobs.variant;
} else if (value) {
acc[e] = value;
}
return acc;
}, {});
const getConfig = function (blobs, { historyCustomLabels }) {
console.log(this);
const res = [
'architecture',
'User',
'created',
'docker_version',
'os',
'Cmd',
'Entrypoint',
'Env',
'Labels',
'User',
'Volumes',
'WorkingDir',
'author',
'id',
'ExposedPorts',
].reduce(function (acc, e) {
const value = blobs[e] || blobs.config[e];
if (value && e === 'architecture' && blobs.variant) {
acc[e] = value + blobs.variant;
} else if (value) {
acc[e] = value;
}
return acc;
}, {});

if (!res.author && (res.Labels && res.Labels.maintainer)) {
if (!res.author && res.Labels && res.Labels.maintainer) {
res.author = blobs.config.Labels.maintainer;
delete res.Labels.maintainer;
}

historyCustomLabels
.filter((label) => res.Labels[label])
.forEach((label) => {
res[`custom-label-${label}`] = res.Labels[label];
delete res.Labels[label];
});

return res;
};
</script>
</tag-history>
</tag-history>
Loading