Last active
May 2, 2023 19:46
-
-
Save spagettikod/b10ea9ed06ce4c328f05935f51055a36 to your computer and use it in GitHub Desktop.
Export swarm stack as Docker Compose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
list_stacks() { | |
echo $(docker stack ls --format '{{ .Name }}') | |
} | |
# returns a list of identifiers for all services in a stack | |
list_stack_service_ids () { | |
echo $(docker service ls --filter label=com.docker.stack.namespace=$1 -q) | |
} | |
# Returns a list of unique identifiers for secrets used in a stack | |
list_stack_secret_ids () { | |
local service_ids="$(list_stack_service_ids $1)" | |
local secret_ids=$(docker service inspect --format '{{- range .Spec.TaskTemplate.ContainerSpec.Secrets }} | |
{{ .SecretID }} | |
{{- end }}' $service_ids) | |
# make the list unique | |
echo $secret_ids | xargs -n1 | sort -u | xargs | |
} | |
# Returns a list of unique identifiers for volumes used in a stack | |
list_stack_volume_names () { | |
local service_ids="$(list_stack_service_ids $1)" | |
local config_ids=$(docker service inspect --format '{{- range .Spec.TaskTemplate.ContainerSpec.Mounts }} | |
{{- if eq .Type "volume" }} | |
{{ .Source }} | |
{{- end }}{{- end }}' $service_ids) | |
# make the list unique | |
echo $config_ids | xargs -n1 | sort -u | xargs | |
} | |
# Returns a list of unique identifiers for configs used in a stack | |
list_stack_config_ids () { | |
local service_ids="$(list_stack_service_ids $1)" | |
local config_ids=$(docker service inspect --format '{{- range .Spec.TaskTemplate.ContainerSpec.Configs }} | |
{{ .ConfigID }} | |
{{- end}}' $service_ids) | |
# make the list unique | |
echo $config_ids | xargs -n1 | sort -u | xargs | |
} | |
# Outputs the Docker compose top-level services element. | |
export_services () { | |
local service_ids="$(list_stack_service_ids $1)" | |
local services="$(docker service inspect --format ' {{ .Spec.Name }}: | |
image: {{ index (split .Spec.TaskTemplate.ContainerSpec.Image "@") 0 }} | |
{{- if .Spec.EndpointSpec.Ports }} | |
ports: | |
{{- range .Spec.EndpointSpec.Ports }} | |
- {{ .PublishedPort }}:{{ .TargetPort }} | |
{{- end }} | |
{{- end }} | |
{{- if .Spec.TaskTemplate.ContainerSpec.Mounts }} | |
volumes: | |
{{- $stack := index .Spec.Labels "com.docker.stack.namespace" }} | |
{{- range .Spec.TaskTemplate.ContainerSpec.Mounts }} | |
{{- $source_without_stack := slice .Source (len (print ($stack) "_")) }} | |
{{- if eq .Type "volume" }} | |
- {{ $source_without_stack }}:{{ .Target }} | |
{{- end }} | |
{{- end }} | |
{{- end }} | |
deploy: | |
{{- if .Spec.Mode.Replicated }} | |
mode: replicated | |
{{- end }} | |
replicas: {{ .Spec.Mode.Replicated.Replicas }} | |
{{- $first := 0 }} | |
{{- range $key, $value := .Spec.Labels }} | |
{{- if and (ne $key "com.docker.stack.image") (ne $key "com.docker.stack.namespace") }} | |
{{- if eq $first 0 }} | |
labels: | |
{{- $first = 1}} | |
{{- end }} | |
- {{ $key }}="{{ $value }}" | |
{{- end }} | |
{{- end }} | |
{{- if .Spec.TaskTemplate.ContainerSpec.Env }} | |
environment: | |
{{- range .Spec.TaskTemplate.ContainerSpec.Env }} | |
- {{ index (split . "=") 0 }}="{{ index (split . "=") 1 }}" | |
{{- end}} | |
{{- end}} | |
{{- if .Spec.TaskTemplate.ContainerSpec.Configs }} | |
configs: | |
{{- range .Spec.TaskTemplate.ContainerSpec.Configs }} | |
- {{ .File.Name }} | |
{{- end }} | |
{{- end }} | |
{{- if .Spec.TaskTemplate.ContainerSpec.Secrets }} | |
secrets: | |
{{- range .Spec.TaskTemplate.ContainerSpec.Secrets }} | |
- {{ .File.Name }} | |
{{- end }} | |
{{- end }} | |
' $service_ids)" | |
printf "services:\n$services" | |
} | |
# Outputs the Docker compose top-level configs element. Configs | |
# will be created from files in the subfolder "configs". | |
export_configs () { | |
local config_ids="$(list_stack_config_ids $1)" | |
# TODO fix the extra line break between secrets | |
local configs="$(docker config inspect --format '{{ $name_without_stack := slice .Spec.Name (len (print (index .Spec.Labels "com.docker.stack.namespace") "_")) -}} | |
{{ print " " }}{{ $name_without_stack -}}: | |
file: configs/{{ .Spec.Name -}}' $config_ids)" | |
printf "configs:\n$configs" | |
} | |
# Outputs the Docker compose top-level secrets element. Secrets | |
# will be created from files in the subfolder "secrets". | |
export_secrets () { | |
local secret_ids="$(list_stack_secret_ids $1)" | |
local secrets="$(docker secret inspect --format '{{ $name_without_stack := slice .Spec.Name (len (print (index .Spec.Labels "com.docker.stack.namespace") "_")) -}} | |
{{ print " " }}{{ $name_without_stack }}: | |
file: secrets/{{ .Spec.Name }}' $secret_ids)" | |
printf "secrets:\n$secrets" | |
} | |
export_volumes () { | |
local volume_names="$(list_stack_volume_names $1)" | |
local volumes="$(docker volume inspect --format '{{ $name_without_stack := slice .Name (len (print (index .Labels "com.docker.stack.namespace") "_")) -}} | |
{{ print " " }}{{ $name_without_stack }}: | |
driver_opts: | |
type: "{{ .Options.type }}" | |
o: "{{ .Options.o }}" | |
device: "{{ .Options.device }}"' $volume_names)" | |
printf "volumes:\n$volumes" | |
} | |
export_stack () { | |
printf "version: \"3.9\"\n" | |
printf "$(export_services $1)\n\n" | |
printf "$(export_configs $1)\n\n" | |
printf "$(export_secrets $1)\n\n" | |
printf "$(export_volumes $1)\n\n" | |
} | |
export_stack $1 | |
exit 0 | |
# STACK_FOLDER=backup | |
# STACKS=$(list_stacks) | |
# mkdir -p $STACK_FOLDER | |
# for i in $STACKS | |
# do | |
# file="${STACK_FOLDER}/${i}-docker-compose.yml" | |
# # printf "$(list_stack_volume_names $1)" | |
# printf "version: \"3.9\"\n" > $file | |
# printf "$(export_services $i)\n\n" >> $file | |
# printf "$(export_configs $i)\n\n" >> $file | |
# printf "$(export_secrets $i)\n\n" >> $file | |
# printf "$(export_volumes $i)\n\n" >> $file | |
# done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment