docker / docker/cli

Go templates can throw unexpected 'index out of range' exceptions

Open
#3,882 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

kind/bug status/0-triage
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
  1. Save the demo YAML as demo.yml
  2. Deploy the stack: docker stack deploy -c demo.yml this_is_a_demo
  3. Check the logs: docker service logs this_is_a_demo_amazing_service | grep X_.* | sort -t'|' -k2 | awk -F'|' '!a[$2]++'
  4. 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
  1. Uncomment line 14 X_4_FAILS in demo.yml and deploy again.
  2. Observe that despite the length of .Service.Name clearly 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
  1. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.