@@ -27,15 +27,15 @@ import (
2727 "os"
2828 "path"
2929 "path/filepath"
30+ "sort"
3031 "strings"
3132 "time"
3233
33- "github.com/pelletier/go-toml"
34- "github.com/pkg/errors"
35-
3634 "github.com/containerd/containerd/errdefs"
3735 "github.com/containerd/containerd/log"
3836 "github.com/containerd/containerd/remotes/docker"
37+ "github.com/pelletier/go-toml"
38+ "github.com/pkg/errors"
3939)
4040
4141// UpdateClientFunc is a function that lets you to amend http Client behavior used by registry clients.
@@ -317,6 +317,11 @@ func parseHostsFile(baseDir string, b []byte) ([]hostConfig, error) {
317317 HostConfigs map [string ]hostFileConfig `toml:"host"`
318318 }{}
319319
320+ orderedHosts , err := getSortedHosts (tree )
321+ if err != nil {
322+ return nil , err
323+ }
324+
320325 var (
321326 hosts []hostConfig
322327 )
@@ -325,22 +330,24 @@ func parseHostsFile(baseDir string, b []byte) ([]hostConfig, error) {
325330 return nil , err
326331 }
327332
328- // Parse root host config
329- parsed , err := parseHostConfig (c .Server , baseDir , c .HostFileConfig )
330- if err != nil {
331- return nil , err
332- }
333- hosts = append (hosts , parsed )
334-
335333 // Parse hosts array
336- for host , config := range c .HostConfigs {
334+ for _ , host := range orderedHosts {
335+ config := c .HostConfigs [host ]
336+
337337 parsed , err := parseHostConfig (host , baseDir , config )
338338 if err != nil {
339339 return nil , err
340340 }
341341 hosts = append (hosts , parsed )
342342 }
343343
344+ // Parse root host config and append it as the last element
345+ parsed , err := parseHostConfig (c .Server , baseDir , c .HostFileConfig )
346+ if err != nil {
347+ return nil , err
348+ }
349+ hosts = append (hosts , parsed )
350+
344351 return hosts , nil
345352}
346353
@@ -464,6 +471,26 @@ func parseHostConfig(server string, baseDir string, config hostFileConfig) (host
464471 return result , nil
465472}
466473
474+ // getSortedHosts returns the list of hosts as they defined in the file.
475+ func getSortedHosts (root * toml.Tree ) ([]string , error ) {
476+ iter , ok := root .Get ("host" ).(* toml.Tree )
477+ if ! ok {
478+ return nil , errors .Errorf ("invalid `host` tree" )
479+ }
480+
481+ list := append ([]string {}, iter .Keys ()... )
482+
483+ // go-toml stores TOML sections in the map object, so no order guaranteed.
484+ // We retrieve line number for each key and sort the keys by position.
485+ sort .Slice (list , func (i , j int ) bool {
486+ h1 := iter .GetPath ([]string {list [i ]}).(* toml.Tree )
487+ h2 := iter .GetPath ([]string {list [j ]}).(* toml.Tree )
488+ return h1 .Position ().Line < h2 .Position ().Line
489+ })
490+
491+ return list , nil
492+ }
493+
467494// makeStringSlice is a helper func to convert from []interface{} to []string.
468495// Additionally an optional cb func may be passed to perform string mapping.
469496func makeStringSlice (slice []interface {}, cb func (string ) string ) ([]string , error ) {
0 commit comments