Skip to content

Instantly share code, notes, and snippets.

@danielpetroianu
Last active January 20, 2022 14:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielpetroianu/6e00624436eab3036b0c8b270b359b62 to your computer and use it in GitHub Desktop.
Save danielpetroianu/6e00624436eab3036b0c8b270b359b62 to your computer and use it in GitHub Desktop.
Generate Homer services from docker labels. Inspired by https://github.com/pawelmalak/flame
#!/bin/bash
_DOCKER_LABEL_NAME="homer.service.name"
_DOCKER_LABEL_URL="homer.service.url"
_DI_RESULT_JSONKEY_CATEGORY="category"
_DI_RESULT_JSONKEY_CATEGORY_ICON="category_icon"
_DI_RESULT_JSONKEY_CATEGORY_ORDER="category_order_index"
_DI_RESULT_JSONKEY_ICON="icon"
_DI_RESULT_JSONKEY_NAME="name"
_DI_RESULT_JSONKEY_DESCRIPTION="description"
_DI_RESULT_JSONKEY_URL="url"
_DI_RESULT_JSONKEY_URL_TARGET="url_target" # https://www.w3schools.com/tags/att_a_target.asp
_DI_RESULT_JSONKEY_ORDER="order_index"
function findDockerContainers() {
# get all containers that want to be displayed in Homer
docker ps --filter "label=$_DOCKER_LABEL_URL" --format "{{.Names}}"
}
function readDockerContainersLables() {
# get the labels from the passed containers and output them as json
function _jsonDockerInspectResult() {
jq '{
'"$_DI_RESULT_JSONKEY_CATEGORY"' : (."homer.service.category.name" // "Apps"),
'"$_DI_RESULT_JSONKEY_CATEGORY_ICON"' : (."homer.service.category.icon" // ""),
'"$_DI_RESULT_JSONKEY_CATEGORY_ORDER"': (."homer.service.category.order_index" // "999"),
'"$_DI_RESULT_JSONKEY_ICON"' : (."homer.service.icon" // ""),
'"$_DI_RESULT_JSONKEY_NAME"' : (."'"$_DOCKER_LABEL_NAME"'" // ."com.docker.compose.service"),
'"$_DI_RESULT_JSONKEY_DESCRIPTION"' : (."homer.service.description" // ."'"$_DOCKER_LABEL_URL"'"),
'"$_DI_RESULT_JSONKEY_URL"' : (."'"$_DOCKER_LABEL_URL"'"),
'"$_DI_RESULT_JSONKEY_URL_TARGET"' : (."homer.service.url.target" // "_blank"),
'"$_DI_RESULT_JSONKEY_ORDER"' : (."homer.service.order_index" // "999")
}'
}
while read containerName; do
docker inspect $containerName --format='{{json .Config.Labels}}' | _jsonDockerInspectResult
done
}
function groupByCategory() {
# group all containers by the $_DI_RESULT_JSONKEY_CATEGORY
function _readAll() {
local inputs=()
while read input; do
inputs+=( $input )
done
echo ${inputs[@]}
}
_readAll | jq -s '
group_by(.'"$_DI_RESULT_JSONKEY_CATEGORY"')
| map({
'"$_DI_RESULT_JSONKEY_CATEGORY"' : (.[0].'"$_DI_RESULT_JSONKEY_CATEGORY"'),
'"$_DI_RESULT_JSONKEY_CATEGORY_ICON"' : (.[0].'"$_DI_RESULT_JSONKEY_CATEGORY_ICON"'),
'"$_DI_RESULT_JSONKEY_CATEGORY_ORDER"' : (.[0].'"$_DI_RESULT_JSONKEY_CATEGORY_ORDER"'),
items: del(
.[].'"$_DI_RESULT_JSONKEY_CATEGORY"',
.[].'"$_DI_RESULT_JSONKEY_CATEGORY_ICON"',
.[].'"$_DI_RESULT_JSONKEY_CATEGORY_ORDER"'
)
})'
}
function mapToHomerConfigYAML() {
# use jq to generate the structure, in JSON, that homer expects
# and converted it to yaml
jq -r '{
services: [
.
| sort_by(.'"$_DI_RESULT_JSONKEY_CATEGORY_ORDER"', .'"$_DI_RESULT_JSONKEY_CATEGORY"')
| .[]
| {
name: (.'"$_DI_RESULT_JSONKEY_CATEGORY"'),
icon: (.'"$_DI_RESULT_JSONKEY_CATEGORY_ICON"' // "" | if . == "" then "" else "fas \(.)" end),
items: [
.items
| sort_by(.'"$_DI_RESULT_JSONKEY_ORDER"', .'"$_DI_RESULT_JSONKEY_NAME"')
| .[]
| {
name: .'"$_DI_RESULT_JSONKEY_NAME"',
icon: (.'"$_DI_RESULT_JSONKEY_ICON"' // "" | if . == "" then "" else "fas \(.)" end),
subtitle: .'"$_DI_RESULT_JSONKEY_DESCRIPTION"',
url: .'"$_DI_RESULT_JSONKEY_URL"',
target: .'"$_DI_RESULT_JSONKEY_URL_TARGET"',
}
]
}
]
}' \
| yq --prettyPrint e '.' - # convert to yaml
}
function writeToHomerConfig() {
local configFile="$1"
# delete the current services
yq -i e 'del(.services)' "$configFile"
# add the new services
yq e '.' - >> "$configFile"
}
# main
# some input checks
if ! command -v jq &> /dev/null; then
echo "ERROR:"
echo " 'jq' could not be found."
echo " see https://stedolan.github.io/jq/download/ for how to install it."
exit 1
fi
if ! command -v yq &> /dev/null; then
echo "ERROR:"
echo " 'yq' could not be found."
echo " see https://mikefarah.gitbook.io/yq/#install for how to install it."
exit 1
fi
HOMER_CONFIG_FILE="$1"
if [[ -z "$HOMER_CONFIG_FILE" ]]; then
echo "ERROR:"
echo " Pass the path to the homer config file"
echo ""
echo " Usage:"
echo " $0 path/to/homer/config.yml"
exit 1
fi
if [[ ! -f "$HOMER_CONFIG_FILE" ]]; then
echo "ERROR:"
echo " File not found at '$HOMER_CONFIG_FILE'"
exit 1
fi
# do the job
findDockerContainers \
| readDockerContainersLables \
| groupByCategory \
| mapToHomerConfigYAML \
| writeToHomerConfig $HOMER_CONFIG_FILE
echo "Done"
echo "Check $HOMER_CONFIG_FILE"
@danielpetroianu
Copy link
Author

danielpetroianu commented Jan 8, 2022

Still learning Bash, so for sure things can be improved.
Tested on Raspbian 10 (buster)

With that in mind, this is how I'm using it:

  1. add the following labels to your container:
    (i'm adding them via docker compose)
portainer:
    image: portainer/portainer-ce
    ...
    labels:
      "homer.service.category.name": "Applications" // optional, default to: "Apps"
      "homer.service.category.icon": "fa-cloud"  // optional, default to: ""
      "homer.service.category.order_index": 0 // optional, default to: 999 - used to order the category
      "homer.service.icon": "fa-docker" // optional, default to: ""
      "homer.service.name": "Portainer" // optional-ish, default to: "com.docker.compose.service" if docker compose was used
      "homer.service.description": "" // optional, default to : "homer.service.url",
      "homer.service.url": "http://mypi.local:9000" // required
      "homer.service.url.target": "" // optional, default to "_blank"
      "homer.service.order_index": 999 // optional, default to: 999 - used to order service in a category
  1. run
docker-homer-generator.sh .config/appdata/homer/config.yml

and .config/appdata/homer/config.yml should now contain

services:
  - name: Applications # from `homer.service.category.name` docker container label
    icon: 'fas fa-cloud' # from `homer.service.category.icon` docker container label
    items:
      - name: Portainer # from `homer.service.name` docker container label
        icon: fas fa-docker # from `homer.service.icon` docker container label
        subtitle: http://mypi.local:9000 # from `homer.service.description` docker container label
        url: http://mypi.local:9000 # from `homer.service.url` docker container label
        target: _blank # from `homer.service.url.target` docker container label

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment