# # DockerBashOOP - Implementation of BashOOP for Docker. # Copyright (C) 2022 Ad5001 # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Only true when some containers matching all filters passed to the object have been found. # Type: bool property Container.found false # Count of containers matching all filters found. # Type: int property Container.count 0 # Summary of all informations found on containers. # Containers are separated by a new line and values by " ; ". property Container.lines # Docker container ids of matched containers. property Container.id # Docker container images of matched containers. property Container.image # Dates of creation of matched containers. property Container.created # Statuses summary of matched containers. property Container.status # List of ports of matched containers. property Container.ports # Names of matched containers. property Container.name # Constructor to fetch one or many running container. # Arguments are the various filters to find containers. See: Container.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: ([string[] filters]) Container.constructor() { 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 else echo -n "Found $(this.count) containers matching filters: " echo $(this.name) fi } # Finds all containers in "docker ps" with a certain field matching a value and returns their lines. # Signature: (, , ) -> string Container.findLineByValue() { fieldId=$1 operation=$2 fieldValue=$3 # 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: Container.filter for reference. # Signature: (, [string filterValue]) Container._baseFilter() { filterName=$1 filterValue=$2 #echo "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 = "$(Container.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 Container._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]) -> void Container.filter() { filterName=$1 filterValue=$2 this._baseFilter $filterName $filterValue this._updateProperties } # Applies a docker command to all containers # Signature: (, [string[] arguments]) -> void|string Container.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]) -> void Container.kill() { $this.applyToAll kill $@ } # Removes all containers assigned to this instance. # Signature: ([string[] arguments]) -> void Container.rm() { $this.applyToAll rm $@ } # Removes all containers assigned to this instance. # Signature: ([string[] arguments]) -> void Container.remove() { $this.applyToAll rm $@ } # Waits for all containers assigned to this instance to stop and echo their exit code. # Signature: ([string[] arguments]) -> void Container.wait() { $this.applyToAll wait $@ } # Displays the logs of the containers # Signature: ([string[] arguments]) -> string Container.logs() { $this.applyToAll logs $@ } # Starts all stopped containers # Signature: ([string[] arguments]) -> string Container.start() { $this.applyToAll start $@ } # Executes a command to all the containers assigned to the instance. # Signature: ([string[] options] ) -> void|string Container.exec() { cmd=${@[-1]} options=${@:$(exec ${#@} - 1)} for container in $(this.id); do $(Docker.Utils.DockerCommand) exec $args $container $cmd done }