Enhance Nextcloud Fixes, Update README #4
Enhance Nextcloud Fixes, Update README
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,3 +7,5 @@ Volumes/*
 | 
				
			|||||||
# Ignore logs
 | 
					# Ignore logs
 | 
				
			||||||
logs/*
 | 
					logs/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ignore MailServer Files
 | 
				
			||||||
 | 
					Config/MailServer/setup.sh
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,240 +0,0 @@
 | 
				
			|||||||
#! /bin/bash
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# version   v1.0.0
 | 
					 | 
				
			||||||
# executed  manually / via Make
 | 
					 | 
				
			||||||
# task      wrapper for various setup scripts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CONFIG_PATH=
 | 
					 | 
				
			||||||
CONTAINER_NAME=
 | 
					 | 
				
			||||||
CRI=
 | 
					 | 
				
			||||||
DEFAULT_CONFIG_PATH=
 | 
					 | 
				
			||||||
DESIRED_CONFIG_PATH=
 | 
					 | 
				
			||||||
DIR=$(pwd)
 | 
					 | 
				
			||||||
DMS_CONFIG='/tmp/docker-mailserver'
 | 
					 | 
				
			||||||
IMAGE_NAME=
 | 
					 | 
				
			||||||
DEFAULT_IMAGE_NAME='docker.io/mailserver/docker-mailserver:latest'
 | 
					 | 
				
			||||||
INFO=
 | 
					 | 
				
			||||||
PODMAN_ROOTLESS=false
 | 
					 | 
				
			||||||
USE_SELINUX=
 | 
					 | 
				
			||||||
USE_TTY=
 | 
					 | 
				
			||||||
VOLUME=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RED=$(echo -ne '\e[31m\e[1m')
 | 
					 | 
				
			||||||
WHITE=$(echo -ne '\e[37m')
 | 
					 | 
				
			||||||
ORANGE=$(echo -ne '\e[38;5;214m')
 | 
					 | 
				
			||||||
LBLUE=$(echo -ne '\e[94m')
 | 
					 | 
				
			||||||
RESET=$(echo -ne '\e[0m')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
set -euEo pipefail
 | 
					 | 
				
			||||||
shopt -s inherit_errexit 2>/dev/null || true
 | 
					 | 
				
			||||||
trap '__err "${BASH_SOURCE}" "${FUNCNAME[0]:-?}" "${BASH_COMMAND:-?}" "${LINENO:-?}" "${?:-?}"' ERR
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function __err
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  [[ ${5} -gt 1 ]] && exit 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  local ERR_MSG="\n--- ${RED}UNCHECKED ERROR${RESET}"
 | 
					 | 
				
			||||||
  ERR_MSG+="\n  - script    = ${1}"
 | 
					 | 
				
			||||||
  ERR_MSG+="\n  - function  = ${2}"
 | 
					 | 
				
			||||||
  ERR_MSG+="\n  - command   = ${3}"
 | 
					 | 
				
			||||||
  ERR_MSG+="\n  - line      = ${4}"
 | 
					 | 
				
			||||||
  ERR_MSG+="\n  - exit code = ${5}"
 | 
					 | 
				
			||||||
  ERR_MSG+='\n\nThis should not have happened. Please file a bug report.\n'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  echo -e "${ERR_MSG}"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function _show_local_usage
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  # shellcheck disable=SC2059
 | 
					 | 
				
			||||||
  printf '%s' "${ORANGE}OPTIONS${RESET}
 | 
					 | 
				
			||||||
    ${LBLUE}Config path, container or image adjustments${RESET}
 | 
					 | 
				
			||||||
        -i IMAGE_NAME
 | 
					 | 
				
			||||||
            Provides the name of the 'docker-mailserver' image. The default value is
 | 
					 | 
				
			||||||
            '${WHITE}${DEFAULT_IMAGE_NAME}${RESET}'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        -c CONTAINER_NAME
 | 
					 | 
				
			||||||
            Provides the name of the running container.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        -p PATH
 | 
					 | 
				
			||||||
            Provides the local path of the config folder to the temporary container instance.
 | 
					 | 
				
			||||||
            Does not work if an existing a 'docker-mailserver' container is already running.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ${LBLUE}SELinux${RESET}
 | 
					 | 
				
			||||||
        -z
 | 
					 | 
				
			||||||
            Allows container access to the bind mount content that is shared among
 | 
					 | 
				
			||||||
            multiple containers on a SELinux-enabled host.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        -Z
 | 
					 | 
				
			||||||
            Allows container access to the bind mount content that is private and
 | 
					 | 
				
			||||||
            unshared with other containers on a SELinux-enabled host.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ${LBLUE}Podman${RESET}
 | 
					 | 
				
			||||||
        -R
 | 
					 | 
				
			||||||
            Accept running in Podman rootless mode. Ignored when using Docker / Docker Compose.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [[ ${1:-} == 'no-exit' ]] && return 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # shellcheck disable=SC2059
 | 
					 | 
				
			||||||
  printf '%s' "${ORANGE}EXIT STATUS${RESET}
 | 
					 | 
				
			||||||
    Exit status is 0 if the command was successful. If there was an unexpected error, an error
 | 
					 | 
				
			||||||
    message is shown describing the error. In case of an error, the script will exit with exit
 | 
					 | 
				
			||||||
    status 1.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function _get_absolute_script_directory
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if dirname "$(readlink -f "${0}")" &>/dev/null
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    DIR=$(dirname "$(readlink -f "${0}")")
 | 
					 | 
				
			||||||
  elif realpath -e -L "${0}" &>/dev/null
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    DIR=$(realpath -e -L "${0}")
 | 
					 | 
				
			||||||
    DIR="${DIR%/setup.sh}"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function _set_default_config_path
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if [[ -d "${DIR}/config" ]]
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    # legacy path (pre v10.2.0)
 | 
					 | 
				
			||||||
    DEFAULT_CONFIG_PATH="${DIR}/config"
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    DEFAULT_CONFIG_PATH="${DIR}/docker-data/dms/config"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function _handle_config_path
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if [[ -z ${DESIRED_CONFIG_PATH} ]]
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    # no desired config path
 | 
					 | 
				
			||||||
    if [[ -n ${CONTAINER_NAME} ]]
 | 
					 | 
				
			||||||
    then
 | 
					 | 
				
			||||||
      VOLUME=$(${CRI} inspect "${CONTAINER_NAME}" \
 | 
					 | 
				
			||||||
        --format="{{range .Mounts}}{{ println .Source .Destination}}{{end}}" | \
 | 
					 | 
				
			||||||
        grep "${DMS_CONFIG}$" 2>/dev/null || :)
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if [[ -n ${VOLUME} ]]
 | 
					 | 
				
			||||||
    then
 | 
					 | 
				
			||||||
      CONFIG_PATH=$(echo "${VOLUME}" | awk '{print $1}')
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if [[ -z ${CONFIG_PATH} ]]
 | 
					 | 
				
			||||||
    then
 | 
					 | 
				
			||||||
      CONFIG_PATH=${DEFAULT_CONFIG_PATH}
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    CONFIG_PATH=${DESIRED_CONFIG_PATH}
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function _run_in_new_container
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  # start temporary container with specified image
 | 
					 | 
				
			||||||
  if ! ${CRI} history -q "${IMAGE_NAME}" &>/dev/null
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    echo "Image '${IMAGE_NAME}' not found. Pulling ..."
 | 
					 | 
				
			||||||
    ${CRI} pull "${IMAGE_NAME}"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ${CRI} run --rm "${USE_TTY}" \
 | 
					 | 
				
			||||||
    -v "${CONFIG_PATH}:${DMS_CONFIG}${USE_SELINUX}" \
 | 
					 | 
				
			||||||
    "${IMAGE_NAME}" "${@}"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function _main
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  _get_absolute_script_directory
 | 
					 | 
				
			||||||
  _set_default_config_path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  local OPTIND
 | 
					 | 
				
			||||||
  while getopts ":c:i:p:zZR" OPT
 | 
					 | 
				
			||||||
  do
 | 
					 | 
				
			||||||
    case ${OPT} in
 | 
					 | 
				
			||||||
      ( i )     IMAGE_NAME="${OPTARG}"     ;;
 | 
					 | 
				
			||||||
      ( z | Z ) USE_SELINUX=":${OPT}"      ;;
 | 
					 | 
				
			||||||
      ( c )     CONTAINER_NAME="${OPTARG}" ;;
 | 
					 | 
				
			||||||
      ( R )     PODMAN_ROOTLESS=true       ;;
 | 
					 | 
				
			||||||
      ( p )
 | 
					 | 
				
			||||||
        case "${OPTARG}" in
 | 
					 | 
				
			||||||
          ( /* ) DESIRED_CONFIG_PATH="${OPTARG}"        ;;
 | 
					 | 
				
			||||||
          ( *  ) DESIRED_CONFIG_PATH="${DIR}/${OPTARG}" ;;
 | 
					 | 
				
			||||||
        esac
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if [[ ! -d ${DESIRED_CONFIG_PATH} ]]
 | 
					 | 
				
			||||||
        then
 | 
					 | 
				
			||||||
          echo "Specified directory '${DESIRED_CONFIG_PATH}' doesn't exist" >&2
 | 
					 | 
				
			||||||
          exit 1
 | 
					 | 
				
			||||||
        fi
 | 
					 | 
				
			||||||
        ;;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      ( * )
 | 
					 | 
				
			||||||
        echo "Invalid option: '-${OPTARG}'" >&2
 | 
					 | 
				
			||||||
        echo -e "Use './setup.sh help' to get a complete overview.\n" >&2
 | 
					 | 
				
			||||||
        _show_local_usage 'no-exit'
 | 
					 | 
				
			||||||
        exit 1
 | 
					 | 
				
			||||||
        ;;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    esac
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
  shift $(( OPTIND - 1 ))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if command -v docker &>/dev/null
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    CRI=docker
 | 
					 | 
				
			||||||
  elif command -v podman &>/dev/null
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    CRI=podman
 | 
					 | 
				
			||||||
    if ! ${PODMAN_ROOTLESS} && [[ ${EUID} -ne 0 ]]
 | 
					 | 
				
			||||||
    then
 | 
					 | 
				
			||||||
      read -r -p "You are running Podman in rootless mode. Continue? [Y/n] "
 | 
					 | 
				
			||||||
      [[ -n ${REPLY} ]] && [[ ${REPLY} =~ (n|N) ]] && exit 0
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    echo 'No supported Container Runtime Interface detected.'
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  INFO=$(${CRI} ps --no-trunc --format "{{.Image}};{{.Names}}" --filter \
 | 
					 | 
				
			||||||
    label=org.opencontainers.image.title="docker-mailserver" | tail -1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [[ -z ${CONTAINER_NAME} ]] && CONTAINER_NAME=${INFO#*;}
 | 
					 | 
				
			||||||
  [[ -z ${IMAGE_NAME} ]] && IMAGE_NAME=${INFO%;*}
 | 
					 | 
				
			||||||
  if [[ -z ${IMAGE_NAME} ]]
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    IMAGE_NAME=${NAME:-${DEFAULT_IMAGE_NAME}}
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if test -t 0
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    USE_TTY="-it"
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    # GitHub Actions will fail (or really anything else
 | 
					 | 
				
			||||||
    #   lacking an interactive tty) if we don't set a
 | 
					 | 
				
			||||||
    #   value here; "-t" alone works for these cases.
 | 
					 | 
				
			||||||
    USE_TTY="-t"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  _handle_config_path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if [[ -n ${CONTAINER_NAME} ]]
 | 
					 | 
				
			||||||
  then
 | 
					 | 
				
			||||||
    ${CRI} exec "${USE_TTY}" "${CONTAINER_NAME}" setup "${@}"
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    _run_in_new_container setup "${@}"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [[ ${1:-} == 'help' ]] && _show_local_usage
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[ -z ${1:-} ]] && set 'help'
 | 
					 | 
				
			||||||
_main "${@}"
 | 
					 | 
				
			||||||
@@ -15,5 +15,17 @@ docker exec -it nextcloud-app-1 chown -Rc www-data:www-data .
 | 
				
			|||||||
# 2023-02-12 Just for good measure.
 | 
					# 2023-02-12 Just for good measure.
 | 
				
			||||||
docker exec -itu www-data nextcloud-app-1 ./occ app:update --all
 | 
					docker exec -itu www-data nextcloud-app-1 ./occ app:update --all
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 2023-07-02 
 | 
				
			||||||
 | 
					# This maybe used to exist, but make sure that Files app is correct.
 | 
				
			||||||
 | 
					docker exec -itu www-data nextcloud-app-1 ./occ files:scan --all
 | 
				
			||||||
 | 
					# This one takes a while.
 | 
				
			||||||
 | 
					docker exec -itu www-data nextcloud-app-1 ./occ files:scan-app-data
 | 
				
			||||||
 | 
					# Extras? Have used the commands in the past and may help in the future.
 | 
				
			||||||
 | 
					docker exec -itu www-data nextcloud-app-1 ./occ maintenance:theme:update
 | 
				
			||||||
 | 
					docker exec -itu www-data nextcloud-app-1 ./occ maintenance:repair
 | 
				
			||||||
 | 
					# May alsp be useful but do not have much experience with them.
 | 
				
			||||||
 | 
					docker exec -itu www-data nextcloud-app-1 ./occ versions:cleanup
 | 
				
			||||||
 | 
					docker exec -itu www-data nextcloud-app-1 ./occ files:cleanup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exit 0
 | 
					exit 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										55
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								README.md
									
									
									
									
									
								
							@@ -1,20 +1,51 @@
 | 
				
			|||||||
# My Docker Setup
 | 
					# My Docker Setup
 | 
				
			||||||
Scripting my way into the Docker world.
 | 
					Scripting my way into the Docker world.
 | 
				
			||||||
Was unable to find a good tutorial on using and managing containers so this is what made sense to me based on practicing with `docker-compose`.
 | 
					I was unable to find a good tutorial on using and managing containers so this is what made sense to me based on practice with `docker-compose`.
 | 
				
			||||||
I am still very new to Docker and am likely to make mistakes, but you're welcome to learn with me. ;)
 | 
					I am still new to Docker and am likely to make mistakes, but you're welcome to learn with me. ;)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Disclaimer
 | 
					## Disclaimer
 | 
				
			||||||
Currently the project only focuses on `apt` based operating systems.
 | 
					Currently the project only focuses on `apt` based operating systems.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## How To Use
 | 
					## How To Use
 | 
				
			||||||
`apt install git bash`
 | 
					Most of these commands benefit from being root. Something like a `sudo su -` if you feel comfortable with it.
 | 
				
			||||||
`git clone https://github.com/hyperling/docker /opt/Docker`
 | 
					Otherwise be aware that using sudo may cause file permission conflicts when interacting with the configuration files and folders.
 | 
				
			||||||
`source /opt/Docker/source.env`
 | 
					
 | 
				
			||||||
`install.sh`
 | 
					Install the project dependencies.
 | 
				
			||||||
`create.sh PROJECT_NAME`
 | 
					```
 | 
				
			||||||
`vi /opt/Docker/Config/PROJECT_NAME/docker-compose.yml`
 | 
					apt install git bash
 | 
				
			||||||
`start.sh`
 | 
					```
 | 
				
			||||||
Cross your fingers and hope to profit.
 | 
					
 | 
				
			||||||
 | 
					Clone the project.
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					git clone https://github.com/hyperling/docker /opt/Docker
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Load the environment variables.
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					source /opt/Docker/source.env
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Install docker to the system.
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					install.sh
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Create an area to add a new product.
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					create.sh PROJECT_NAME
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Edit the project's details.
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					vi /opt/Docker/Config/PROJECT_NAME/docker-compose.yml
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Start all of the docker projects.
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					start.sh
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Cross your fingers and hope to profit!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Folders
 | 
					## Folders
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,8 +55,8 @@ Compose projects are set up here. Each folder should have a `docker-compose.yml`
 | 
				
			|||||||
### Volumes
 | 
					### Volumes
 | 
				
			||||||
The data of the files go here if the Config is done correctly.
 | 
					The data of the files go here if the Config is done correctly.
 | 
				
			||||||
I think this should be easier to remember than `/var/lib/docker/volumes` when it comes time for migrations.
 | 
					I think this should be easier to remember than `/var/lib/docker/volumes` when it comes time for migrations.
 | 
				
			||||||
Hopefully all that's needed is to rsync `/opt/Docker` and run start.sh on the new server.
 | 
					Hopefully all that'd be needed is to rsync `/opt/Docker` and run `install.sh` and then `start.sh` on the new server.
 | 
				
			||||||
That's my opinion though, if someone else uses this then they can use `/var/`.
 | 
					That's my opinion though, if someone else uses this then they are welcome to place it where they'd like.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### bin
 | 
					### bin
 | 
				
			||||||
Scripts to help make life easier. Some are pretty basic, but others do nice things like handle the container IDs.
 | 
					Scripts to help make life easier. Some are pretty basic, but others do nice things like handle the container IDs.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user