Go templates can throw unexpected 'index out of range' exceptions
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 6.1k
- Forks
- 2.2k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 43
Description
Description
When trying to use some Go functions along with a template, it is possible to hit an unexpected index out of range if you try to use an index greater than 11. For example, this will always fail:
X_4_FAILS: "{{ slice .Service.Name 12 }}"
This is a basic Stack YAML to demonstrate the behaviour:
version: '3.8'
services:
amazing_service:
image: bash:latest
command: "env"
environment:
# This shows the length to be 30
X_1_VAR_LENGTH: "{{ (.Service.Name|len) }}"
# As the length is 30, these both work
X_2_WORKS: "{{ slice .Service.Name 10 }}"
X_3_WORKS: "{{ slice .Service.Name 11 }}"
# This should work, but doesn't
#X_4_FAILS: "{{ slice .Service.Name 12 }}"
# This is a work around, the minimal check is that len ge 11, I use 15 here as I want the 15th index and wish to be safe
X_5_WORK_AROUND: "{{ if ge (.Service.Name|len) 15 }}{{ slice .Service.Name 15 }}{{end}}"
# If it thinks the len is under 12, can we get a value for that? No
X_6_LE_11: "{{ if le (.Service.Name|len) 11 }}{{ print .Service.Name }}{{end}}"
# This gives a value as expected
X_7_GE_11: "{{ if ge (.Service.Name|len) 11 }}{{ print .Service.Name }}{{end}}"
This has also been show to fail with .Node.Hostname as seen here.
Both those examples are with stack deploy, but it has also be seen with docker container ls as demonstrated here. This comment shows a static string and template of equivalent values being treated differently.
Reproduce
- Save the demo YAML as
demo.yml - Deploy the stack:
docker stack deploy -c demo.yml this_is_a_demo - Check the logs:
docker service logs this_is_a_demo_amazing_service | grep X_.* | sort -t'|' -k2 | awk -F'|' '!a[$2]++' - Results will be similar to:
this_is_a_demo_amazing_service.1.02s2t1pzn56n@worker1 | X_1_VAR_LENGTH=30
this_is_a_demo_amazing_service.1.02s2t1pzn56n@worker1 | X_2_WORKS=demo_amazing_service
this_is_a_demo_amazing_service.1.02s2t1pzn56n@worker1 | X_3_WORKS=emo_amazing_service
this_is_a_demo_amazing_service.1.02s2t1pzn56n@worker1 | X_5_WORK_AROUND=amazing_service
this_is_a_demo_amazing_service.1.02s2t1pzn56n@worker1 | X_6_LE_11=
this_is_a_demo_amazing_service.1.02s2t1pzn56n@worker1 | X_7_GE_11=this_is_a_demo_amazing_service
- Uncomment line 14
X_4_FAILSindemo.ymland deploy again. - Observe that despite the length of
.Service.Nameclearly being 30, one gets the following error:
Updating service this_is_a_demo_amazing_service (id: sbes2qloecdxcvtegtoca33iq)
failed to update service this_is_a_demo_amazing_service: Error response from daemon: rpc error: code = InvalidArgument desc = expanding env failed: expanding env "FAILS1={{ slice .Service.Name 12 }}": template: expansion:1:3: executing "expansion" at <slice .Service.Name 12>: error calling slice: index out of range: 12
- Remove the stack:
docker stack rm this_is_a_demo
Expected behavior
The function should return the requested slice and not error.
docker version
Client: Docker Engine - Community
Version: 20.10.13
API version: 1.41
Go version: go1.16.15
Git commit: a224086
Built: Thu Mar 10 14:09:51 2022
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.13
API version: 1.41 (minimum version 1.12)
Go version: go1.16.15
Git commit: 906f57f
Built: Thu Mar 10 14:08:16 2022
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: 1.5.10
GitCommit: 2a1d4dbdb2a1030dc5b01e96fb110a9d9f150ecc
runc:
Version: 1.0.3
GitCommit: v1.0.3-0-gf46b6ba
docker-init:
Version: 0.19.0
GitCommit: de40ad0
docker info
Client:
Context: default
Debug Mode: false
Plugins:
app: Docker App (Docker Inc., v0.9.1-beta3)
buildx: Docker Buildx (Docker Inc., v0.8.0-docker)
scan: Docker Scan (Docker Inc., v0.17.0)
Server:
Containers: 26
Running: 20
Paused: 0
Stopped: 6
Images: 126
SubnetSize: 24
Data Path Port: 4789
Orchestration:
Task History Retention Limit: 5
Raft:
Snapshot Interval: 10000
Number of Old Snapshots to Retain: 0
Heartbeat Tick: 1
Election Tick: 10
Dispatcher:
Heartbeat Period: 5 seconds
CA Configuration:
Expiry Duration: 3 months
Force Rotate: 0
Autolock Managers: false
Root Rotation In Progress: false
Node Address: 192.168.22.3
Manager Addresses:
192.168.22.3:2377
Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 2a1d4dbdb2a1030dc5b01e96fb110a9d9f150ecc
runc version: v1.0.3-0-gf46b6ba
init version: de40ad0
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-1160.59.1.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory: 4.685GiB
Name: leader
ID: D7FB:CHET:27PH:ZY5N:5VQ2:7P22:EHNJ:UKCT:CQBW:YAYF:OSOX:2ZGX
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: true
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Additional Info
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the demo.yml reproduction and the stack deploy command, then compare the same Go template behavior through docker container ls. Trace the template expansion entry point shown in the error and verify how string length and slice indexes are handled. Done means indexes within the reported string length return the requested slice without an index-out-of-range error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, go
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100