From ed7dc83f95fb1f0d4e616ef8d9feadea887dfc50 Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Wed, 29 Dec 2021 20:45:55 +0100 Subject: [PATCH] Filtering system to allow multiple containers. --- RunningContainer.shc | 187 +++++++++++++++++++++++++++++++++---------- libraries/oop | 2 +- 2 files changed, 144 insertions(+), 45 deletions(-) diff --git a/RunningContainer.shc b/RunningContainer.shc index 9518f86..9457c7f 100644 --- a/RunningContainer.shc +++ b/RunningContainer.shc @@ -16,7 +16,10 @@ # along with this program. If not, see . # -property RunningContainer.found +property RunningContainer.found false +property RunningContainer.count 0 + +property RunningContainer.lines property RunningContainer.id property RunningContainer.image property RunningContainer.created @@ -24,51 +27,36 @@ property RunningContainer.status property RunningContainer.ports property RunningContainer.name -# Constructor to fetch a running container. -# First argument is what to fetch, and second is matching attribute. -# Example of first arguments: "named" to find by name, "created" to find by creation date, -# "withid" to find by container id, "withimage" to find by image, "withstatus" to check status, "withport" to find all having this port forwarded.... +# Constructor to fetch one or many running container. +# Arguments are the various filters to find containers. See: RunningContainer.filter for reference. # NOTE: Multiple containers can be matched. If so, all properties contain the value for each container per line. # NOTE: If no container is found, a message will be printed, and the constructor will exit with false. -# Signature: (, ) +# Signature: ([string[] filters]) RunningContainer.constructor() { - matchType=$1 - matchValue=$2 - lines="" - case $matchType in - withid) - lines="$(RunningContainer.findLineByValue 1 == $matchValue)" - ;; - withimage) - lines="$(RunningContainer.findLineByValue 2 == $matchValue)" - ;; - created) - lines="$(RunningContainer.findLineByValue 4 == $matchValue)" - ;; - withstatus) - lines="$(RunningContainer.findLineByValue 5 == $matchValue)" - ;; - withport) - lines="$(RunningContainer.findLineByValue 6 ~ $matchValue)" - ;; - named) - lines="$(RunningContainer.findLineByValue 7 == $matchValue)" - if [ -z "$lines" ]; then # Sometimes, when no ports are forwarded, the name is in the 6th field. - lines="$(RunningContainer.findLineByValue 6 == $matchValue)" - fi - ;; - esac - if [ ! -z $lines ]; then - this.found = true - this.id = "$(echo $lines | awk -F " {2,}" '{ print $1 }')" - this.image = "$(echo $lines | awk -F " {2,}" '{ print $2 }')" - this.created = "$(echo $lines | awk -F " {2,}" '{ print $4 }')" - this.status = "$(echo $lines | awk -F " {2,}" '{ print $5 }')" - this.ports = "$(echo $lines | awk -F " {2,}" '{ print $6 }')" - this.name = "$(echo $lines | awk -F " {2,}" '{ print $7 }')" - else - this.found = false - fi + args=( "$@" ) + this.lines = "$($(Docker.Utils.DockerCommand) ps --all | awk -F " {2,}" 'NR>1 { print $1,";",$2,";",$3,";",$4,";",$5,";",$6,($7 != "" ? (";"$7) : "") }')" + # Apply filters + for ((i=0; i < ${#args[@]}; i++)); do + filterName=${args[$i]} + + case $filterName in + with_id | with_image | created | with_status | with_port | named) + # The next argument is the value of the filter, so we skip it. + i=$(expr "$i" + 1) + filterValue=${args[$i]} + this._baseFilter $filterName $filterValue + ;; + running | exited | stopped) + this._baseFilter $filterName + ;; + *) + echo "Unknown filter $filterName." 1>&2; + ;; + esac + done + this._updateProperties + # Print error message in STDERR when no containers are found. + if [ "$(this.found)" == false ]; then echo "No container found matching those filters." 1>&2; fi } # Finds all containers in "docker ps" with a certain field matching a value and returns their lines. @@ -77,7 +65,118 @@ RunningContainer.findLineByValue() { fieldId=$1 operation=$2 fieldValue=$3 - docker ps | awk -F " {2,}" -v value="$fieldValue" 'NR>1 && $'"$fieldId"' '"$operation"' value' + # YES. I know. You shouldn't do that with AWK, but variables, FOR SOME REASON, are not properly passed to AWK. + this.lines | awk -F " ; " '$'"$fieldId"' '"$operation"' "'"$fieldValue"'" { print $1,";",$2,";",$3,";",$4,";",$5,";",$6,($7 != "" ? (";"$7) : "") }' } +# Internal function which filters. See: RunningContainer.filter for reference. +# Signature: (, [string filterValue]) +RunningContainer._baseFilter() { + filterName=$1 + filterValue=$2 + #echo -n "Applying filter $filterName (previous count $(this.lines | wc -l))..." 1>&2 + case $filterName in + with_id) + this.lines = "$(this.findLineByValue 1 == $filterValue)" + ;; + with_image) + this.lines = "$(this.findLineByValue 2 == $filterValue)" + ;; + created) + this.lines = "$(this.findLineByValue 4 == $filterValue)" + ;; + with_status) + this.lines = "$(this.findLineByValue 5 == $filterValue)" + ;; + running) + this.lines = "$(RunningContainer.findLineByValue 5 ~ "Up")" + ;; + exited | stopped) + this.lines = "$(this.findLineByValue 5 "~" "Exited")" + ;; + with_port) + this.lines = "$(this.findLineByValue 6 ~ $filterValue)" + ;; + named) + lines="$(this.findLineByValue 7 "~" $filterValue)" + if [ -z "$lines" ]; then # Sometimes, when no ports are forwarded, the name is in the 6th field. + lines="$(this.findLineByValue 6 "~" $filterValue)" + fi + this.lines = "$lines" + ;; + esac + #echo " New count: $(this.lines | wc -l)." 1>&2 +} + +# Updates property data from fetched lines. +# No argument +RunningContainer._updateProperties() { + if [ ! -z "$(this.lines)" ]; then + this.found = true + this.count = $(this.lines | wc -l) + this.id = "$(this.lines | awk -F " ; " '{ print $1 }')" + this.image = "$(this.lines | awk -F " ; " '{ print $2 }')" + this.created = "$(this.lines | awk -F " ; " '{ print $4 }')" + this.status = "$(this.lines | awk -F " ; " '{ print $5 }')" + this.ports = "$(this.lines | awk -F " ; " '{ if($7 != "") { print $6 } }')" # If field 7 is null, that means field 6 is the name, so there are no ports. + this.name = "$(this.lines | awk -F " ; " '{ print ($7 != "") ? $7 : $6 }')" + else + this.found = false + this.count = 0 + fi +} + +# Filters all containers assigned to the instance and removes all instances not corresponding to it. +# Filters with a value: with_id, with_image, created, with_status, with_port, named +# Filters without value: running, exited +# Filter types: +# - "named" to find by name +# - "created" to find by creation date, +# - "with_id" to find by container id +# - "with_image" to find by image +# - "with_status" to check status +# - "with_port" to find all having this port forwarded +# - "running" to find all containers running +# - "exited" or "stopped" to find all containers stopped. +# Signature: (, [string filterValue]) +RunningContainer.filter() { + filterName=$1 + filterValue=$2 + this._baseFilter $filterName $filterValue + this._updateProperties +} + +# Applies a docker command to all containers +# Signature: (, [string[] arguments]) +RunningContainer.applyToAll() { + cmd=$1 + args=${@:2} + for container in $(this.id); do + $(Docker.Utils.DockerCommand) $cmd $args $container + done +} + +# Kills all containers assigned to this instance. +# Signature: ([string[] arguments]) +RunningContainer.kill() { + $this.applyToAll kill $@ +} + +# Removes all containers assigned to this instance. +# Signature: ([string[] arguments]) +RunningContainer.rm() { + $this.applyToAll rm $@ +} + +# Removes all containers assigned to this instance. +# Signature: ([string[] arguments]) +RunningContainer.remove() { + $this.applyToAll rm $@ +} + +# Waits for all containers assigned to this instance to stop and echo their exit code. +# Signature: ([string[] arguments]) +RunningContainer.wait() { + $this.applyToAll wait $@ +} diff --git a/libraries/oop b/libraries/oop index ba00037..8d92cad 160000 --- a/libraries/oop +++ b/libraries/oop @@ -1 +1 @@ -Subproject commit ba000379d7f736f5ad27a609588e555fe94361d0 +Subproject commit 8d92cad72064b3c3f038d250ba5c2c1a098bfe43