Browse code

Merge pull request #21586 from calavera/remove_runconfig_from_routes

Remove runconfig package dependency from the API.

Arnaud Porterie authored on 2016/03/30 00:40:49
Showing 13 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+package httputils
1
+
2
+import (
3
+	"io"
4
+
5
+	"github.com/docker/engine-api/types/container"
6
+	"github.com/docker/engine-api/types/network"
7
+)
8
+
9
+// ContainerDecoder specifies how
10
+// to translate an io.Reader into
11
+// container configuration.
12
+type ContainerDecoder interface {
13
+	DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error)
14
+	DecodeHostConfig(src io.Reader) (*container.HostConfig, error)
15
+}
... ...
@@ -1,17 +1,22 @@
1 1
 package container
2 2
 
3
-import "github.com/docker/docker/api/server/router"
3
+import (
4
+	"github.com/docker/docker/api/server/httputils"
5
+	"github.com/docker/docker/api/server/router"
6
+)
4 7
 
5 8
 // containerRouter is a router to talk with the container controller
6 9
 type containerRouter struct {
7 10
 	backend Backend
11
+	decoder httputils.ContainerDecoder
8 12
 	routes  []router.Route
9 13
 }
10 14
 
11 15
 // NewRouter initializes a new container router
12
-func NewRouter(b Backend) router.Router {
16
+func NewRouter(b Backend, decoder httputils.ContainerDecoder) router.Router {
13 17
 	r := &containerRouter{
14 18
 		backend: b,
19
+		decoder: decoder,
15 20
 	}
16 21
 	r.initRoutes()
17 22
 	return r
... ...
@@ -16,7 +16,6 @@ import (
16 16
 	"github.com/docker/docker/pkg/ioutils"
17 17
 	"github.com/docker/docker/pkg/signal"
18 18
 	"github.com/docker/docker/pkg/term"
19
-	"github.com/docker/docker/runconfig"
20 19
 	"github.com/docker/engine-api/types"
21 20
 	"github.com/docker/engine-api/types/container"
22 21
 	"github.com/docker/engine-api/types/filters"
... ...
@@ -137,7 +136,7 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
137 137
 			return err
138 138
 		}
139 139
 
140
-		c, err := runconfig.DecodeHostConfig(r.Body)
140
+		c, err := s.decoder.DecodeHostConfig(r.Body)
141 141
 		if err != nil {
142 142
 			return err
143 143
 		}
... ...
@@ -338,7 +337,7 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
338 338
 
339 339
 	name := r.Form.Get("name")
340 340
 
341
-	config, hostConfig, networkingConfig, err := runconfig.DecodeContainerConfig(r.Body)
341
+	config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body)
342 342
 	if err != nil {
343 343
 		return err
344 344
 	}
... ...
@@ -1,17 +1,22 @@
1 1
 package image
2 2
 
3
-import "github.com/docker/docker/api/server/router"
3
+import (
4
+	"github.com/docker/docker/api/server/httputils"
5
+	"github.com/docker/docker/api/server/router"
6
+)
4 7
 
5 8
 // imageRouter is a router to talk with the image controller
6 9
 type imageRouter struct {
7 10
 	backend Backend
11
+	decoder httputils.ContainerDecoder
8 12
 	routes  []router.Route
9 13
 }
10 14
 
11 15
 // NewRouter initializes a new image router
12
-func NewRouter(backend Backend) router.Router {
16
+func NewRouter(backend Backend, decoder httputils.ContainerDecoder) router.Router {
13 17
 	r := &imageRouter{
14 18
 		backend: backend,
19
+		decoder: decoder,
15 20
 	}
16 21
 	r.initRoutes()
17 22
 	return r
... ...
@@ -17,7 +17,6 @@ import (
17 17
 	"github.com/docker/docker/pkg/ioutils"
18 18
 	"github.com/docker/docker/pkg/streamformatter"
19 19
 	"github.com/docker/docker/reference"
20
-	"github.com/docker/docker/runconfig"
21 20
 	"github.com/docker/engine-api/types"
22 21
 	"github.com/docker/engine-api/types/container"
23 22
 	"golang.org/x/net/context"
... ...
@@ -40,7 +39,7 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
40 40
 		pause = true
41 41
 	}
42 42
 
43
-	c, _, _, err := runconfig.DecodeContainerConfig(r.Body)
43
+	c, _, _, err := s.decoder.DecodeConfig(r.Body)
44 44
 	if err != nil && err != io.EOF { //Do not fail if body is empty.
45 45
 		return err
46 46
 	}
... ...
@@ -1,6 +1,8 @@
1 1
 package network
2 2
 
3 3
 import (
4
+	"github.com/docker/engine-api/types"
5
+	"github.com/docker/engine-api/types/filters"
4 6
 	"github.com/docker/engine-api/types/network"
5 7
 	"github.com/docker/libnetwork"
6 8
 )
... ...
@@ -11,8 +13,8 @@ type Backend interface {
11 11
 	FindNetwork(idName string) (libnetwork.Network, error)
12 12
 	GetNetworkByName(idName string) (libnetwork.Network, error)
13 13
 	GetNetworksByID(partialID string) []libnetwork.Network
14
-	GetAllNetworks() []libnetwork.Network
15
-	CreateNetwork(name, driver string, ipam network.IPAM, options map[string]string, labels map[string]string, internal bool, enableIPv6 bool) (libnetwork.Network, error)
14
+	FilterNetworks(netFilters filters.Args) ([]libnetwork.Network, error)
15
+	CreateNetwork(types.NetworkCreate) (*types.NetworkCreateResponse, error)
16 16
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
17 17
 	DisconnectContainerFromNetwork(containerName string, network libnetwork.Network, force bool) error
18 18
 	DeleteNetwork(name string) error
19 19
deleted file mode 100644
... ...
@@ -1,110 +0,0 @@
1
-package network
2
-
3
-import (
4
-	"fmt"
5
-	"regexp"
6
-	"strings"
7
-
8
-	"github.com/docker/docker/runconfig"
9
-	"github.com/docker/engine-api/types/filters"
10
-	"github.com/docker/libnetwork"
11
-)
12
-
13
-type filterHandler func([]libnetwork.Network, string) ([]libnetwork.Network, error)
14
-
15
-var (
16
-	// supportedFilters predefined some supported filter handler function
17
-	supportedFilters = map[string]filterHandler{
18
-		"type": filterNetworkByType,
19
-		"name": filterNetworkByName,
20
-		"id":   filterNetworkByID,
21
-	}
22
-
23
-	// acceptFilters is an acceptable filter flag list
24
-	// generated for validation. e.g.
25
-	// acceptedFilters = map[string]bool{
26
-	//     "type": true,
27
-	//     "name": true,
28
-	//     "id":   true,
29
-	// }
30
-	acceptedFilters = func() map[string]bool {
31
-		ret := make(map[string]bool)
32
-		for k := range supportedFilters {
33
-			ret[k] = true
34
-		}
35
-		return ret
36
-	}()
37
-)
38
-
39
-func filterNetworkByType(nws []libnetwork.Network, netType string) (retNws []libnetwork.Network, err error) {
40
-	switch netType {
41
-	case "builtin":
42
-		for _, nw := range nws {
43
-			if runconfig.IsPreDefinedNetwork(nw.Name()) {
44
-				retNws = append(retNws, nw)
45
-			}
46
-		}
47
-	case "custom":
48
-		for _, nw := range nws {
49
-			if !runconfig.IsPreDefinedNetwork(nw.Name()) {
50
-				retNws = append(retNws, nw)
51
-			}
52
-		}
53
-	default:
54
-		return nil, fmt.Errorf("Invalid filter: 'type'='%s'", netType)
55
-	}
56
-	return retNws, nil
57
-}
58
-
59
-func filterNetworkByName(nws []libnetwork.Network, name string) (retNws []libnetwork.Network, err error) {
60
-	for _, nw := range nws {
61
-		// exact match (fast path)
62
-		if nw.Name() == name {
63
-			retNws = append(retNws, nw)
64
-			continue
65
-		}
66
-
67
-		// regexp match (slow path)
68
-		match, err := regexp.MatchString(name, nw.Name())
69
-		if err != nil || !match {
70
-			continue
71
-		} else {
72
-			retNws = append(retNws, nw)
73
-		}
74
-	}
75
-	return retNws, nil
76
-}
77
-
78
-func filterNetworkByID(nws []libnetwork.Network, id string) (retNws []libnetwork.Network, err error) {
79
-	for _, nw := range nws {
80
-		if strings.HasPrefix(nw.ID(), id) {
81
-			retNws = append(retNws, nw)
82
-		}
83
-	}
84
-	return retNws, nil
85
-}
86
-
87
-// filterAllNetworks filters network list according to user specified filter
88
-// and returns user chosen networks
89
-func filterNetworks(nws []libnetwork.Network, filter filters.Args) ([]libnetwork.Network, error) {
90
-	// if filter is empty, return original network list
91
-	if filter.Len() == 0 {
92
-		return nws, nil
93
-	}
94
-
95
-	var displayNet []libnetwork.Network
96
-	for fkey, fhandler := range supportedFilters {
97
-		errFilter := filter.WalkValues(fkey, func(fval string) error {
98
-			passList, err := fhandler(nws, fval)
99
-			if err != nil {
100
-				return err
101
-			}
102
-			displayNet = append(displayNet, passList...)
103
-			return nil
104
-		})
105
-		if errFilter != nil {
106
-			return nil, errFilter
107
-		}
108
-	}
109
-	return displayNet, nil
110
-}
... ...
@@ -2,13 +2,11 @@ package network
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
-	"fmt"
6 5
 	"net/http"
7 6
 
8 7
 	"golang.org/x/net/context"
9 8
 
10 9
 	"github.com/docker/docker/api/server/httputils"
11
-	"github.com/docker/docker/runconfig"
12 10
 	"github.com/docker/engine-api/types"
13 11
 	"github.com/docker/engine-api/types/filters"
14 12
 	"github.com/docker/engine-api/types/network"
... ...
@@ -26,21 +24,14 @@ func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWrit
26 26
 		return err
27 27
 	}
28 28
 
29
-	if netFilters.Len() != 0 {
30
-		if err := netFilters.Validate(acceptedFilters); err != nil {
31
-			return err
32
-		}
33
-	}
34
-
35 29
 	list := []*types.NetworkResource{}
36 30
 
37
-	nwList := n.backend.GetAllNetworks()
38
-	displayable, err := filterNetworks(nwList, netFilters)
31
+	nwList, err := n.backend.FilterNetworks(netFilters)
39 32
 	if err != nil {
40 33
 		return err
41 34
 	}
42 35
 
43
-	for _, nw := range displayable {
36
+	for _, nw := range nwList {
44 37
 		list = append(list, buildNetworkResource(nw))
45 38
 	}
46 39
 
... ...
@@ -61,7 +52,6 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
61 61
 
62 62
 func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
63 63
 	var create types.NetworkCreate
64
-	var warning string
65 64
 
66 65
 	if err := httputils.ParseForm(r); err != nil {
67 66
 		return err
... ...
@@ -75,31 +65,12 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
75 75
 		return err
76 76
 	}
77 77
 
78
-	if runconfig.IsPreDefinedNetwork(create.Name) {
79
-		return httputils.WriteJSON(w, http.StatusForbidden,
80
-			fmt.Sprintf("%s is a pre-defined network and cannot be created", create.Name))
81
-	}
82
-
83
-	nw, err := n.backend.GetNetworkByName(create.Name)
84
-	if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok {
85
-		return err
86
-	}
87
-	if nw != nil {
88
-		if create.CheckDuplicate {
89
-			return libnetwork.NetworkNameError(create.Name)
90
-		}
91
-		warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
92
-	}
93
-
94
-	nw, err = n.backend.CreateNetwork(create.Name, create.Driver, create.IPAM, create.Options, create.Labels, create.Internal, create.EnableIPv6)
78
+	nw, err := n.backend.CreateNetwork(create)
95 79
 	if err != nil {
96 80
 		return err
97 81
 	}
98 82
 
99
-	return httputils.WriteJSON(w, http.StatusCreated, &types.NetworkCreateResponse{
100
-		ID:      nw.ID(),
101
-		Warning: warning,
102
-	})
83
+	return httputils.WriteJSON(w, http.StatusCreated, nw)
103 84
 }
104 85
 
105 86
 func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -6,8 +6,11 @@ import (
6 6
 	"net/http"
7 7
 	"strings"
8 8
 
9
+	netsettings "github.com/docker/docker/daemon/network"
9 10
 	"github.com/docker/docker/errors"
10 11
 	"github.com/docker/docker/runconfig"
12
+	"github.com/docker/engine-api/types"
13
+	"github.com/docker/engine-api/types/filters"
11 14
 	"github.com/docker/engine-api/types/network"
12 15
 	"github.com/docker/libnetwork"
13 16
 )
... ...
@@ -77,8 +80,8 @@ func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network {
77 77
 	return list
78 78
 }
79 79
 
80
-// GetAllNetworks returns a list containing all networks
81
-func (daemon *Daemon) GetAllNetworks() []libnetwork.Network {
80
+// getAllNetworks returns a list containing all networks
81
+func (daemon *Daemon) getAllNetworks() []libnetwork.Network {
82 82
 	c := daemon.netController
83 83
 	list := []libnetwork.Network{}
84 84
 	l := func(nw libnetwork.Network) bool {
... ...
@@ -91,12 +94,33 @@ func (daemon *Daemon) GetAllNetworks() []libnetwork.Network {
91 91
 }
92 92
 
93 93
 // CreateNetwork creates a network with the given name, driver and other optional parameters
94
-func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM, netOption map[string]string, labels map[string]string, internal bool, enableIPv6 bool) (libnetwork.Network, error) {
94
+func (daemon *Daemon) CreateNetwork(create types.NetworkCreate) (*types.NetworkCreateResponse, error) {
95
+	if runconfig.IsPreDefinedNetwork(create.Name) {
96
+		err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name)
97
+		return nil, errors.NewErrorWithStatusCode(err, http.StatusForbidden)
98
+	}
99
+
100
+	var warning string
101
+	nw, err := daemon.GetNetworkByName(create.Name)
102
+	if err != nil {
103
+		if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok {
104
+			return nil, err
105
+		}
106
+	}
107
+	if nw != nil {
108
+		if create.CheckDuplicate {
109
+			return nil, libnetwork.NetworkNameError(create.Name)
110
+		}
111
+		warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
112
+	}
113
+
95 114
 	c := daemon.netController
115
+	driver := create.Driver
96 116
 	if driver == "" {
97 117
 		driver = c.Config().Daemon.DefaultDriver
98 118
 	}
99 119
 
120
+	ipam := create.IPAM
100 121
 	v4Conf, v6Conf, err := getIpamConfig(ipam.Config)
101 122
 	if err != nil {
102 123
 		return nil, err
... ...
@@ -104,20 +128,23 @@ func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM, netO
104 104
 
105 105
 	nwOptions := []libnetwork.NetworkOption{
106 106
 		libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf, ipam.Options),
107
-		libnetwork.NetworkOptionEnableIPv6(enableIPv6),
108
-		libnetwork.NetworkOptionDriverOpts(netOption),
109
-		libnetwork.NetworkOptionLabels(labels),
107
+		libnetwork.NetworkOptionEnableIPv6(create.EnableIPv6),
108
+		libnetwork.NetworkOptionDriverOpts(create.Options),
109
+		libnetwork.NetworkOptionLabels(create.Labels),
110 110
 	}
111
-	if internal {
111
+	if create.Internal {
112 112
 		nwOptions = append(nwOptions, libnetwork.NetworkOptionInternalNetwork())
113 113
 	}
114
-	n, err := c.NewNetwork(driver, name, nwOptions...)
114
+	n, err := c.NewNetwork(driver, create.Name, nwOptions...)
115 115
 	if err != nil {
116 116
 		return nil, err
117 117
 	}
118 118
 
119 119
 	daemon.LogNetworkEvent(n, "create")
120
-	return n, nil
120
+	return &types.NetworkCreateResponse{
121
+		ID:      n.ID(),
122
+		Warning: warning,
123
+	}, nil
121 124
 }
122 125
 
123 126
 func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnetwork.IpamConf, error) {
... ...
@@ -203,3 +230,15 @@ func (daemon *Daemon) DeleteNetwork(networkID string) error {
203 203
 	daemon.LogNetworkEvent(nw, "destroy")
204 204
 	return nil
205 205
 }
206
+
207
+// FilterNetworks returns a list of networks filtered by the given arguments.
208
+// It returns an error if the filters are not included in the list of accepted filters.
209
+func (daemon *Daemon) FilterNetworks(netFilters filters.Args) ([]libnetwork.Network, error) {
210
+	if netFilters.Len() != 0 {
211
+		if err := netFilters.Validate(netsettings.AcceptedFilters); err != nil {
212
+			return nil, err
213
+		}
214
+	}
215
+	nwList := daemon.getAllNetworks()
216
+	return netsettings.FilterNetworks(nwList, netFilters)
217
+}
206 218
new file mode 100644
... ...
@@ -0,0 +1,110 @@
0
+package network
1
+
2
+import (
3
+	"fmt"
4
+	"regexp"
5
+	"strings"
6
+
7
+	"github.com/docker/docker/runconfig"
8
+	"github.com/docker/engine-api/types/filters"
9
+	"github.com/docker/libnetwork"
10
+)
11
+
12
+type filterHandler func([]libnetwork.Network, string) ([]libnetwork.Network, error)
13
+
14
+var (
15
+	// supportedFilters predefined some supported filter handler function
16
+	supportedFilters = map[string]filterHandler{
17
+		"type": filterNetworkByType,
18
+		"name": filterNetworkByName,
19
+		"id":   filterNetworkByID,
20
+	}
21
+
22
+	// AcceptedFilters is an acceptable filter flag list
23
+	// generated for validation. e.g.
24
+	// acceptedFilters = map[string]bool{
25
+	//     "type": true,
26
+	//     "name": true,
27
+	//     "id":   true,
28
+	// }
29
+	AcceptedFilters = func() map[string]bool {
30
+		ret := make(map[string]bool)
31
+		for k := range supportedFilters {
32
+			ret[k] = true
33
+		}
34
+		return ret
35
+	}()
36
+)
37
+
38
+func filterNetworkByType(nws []libnetwork.Network, netType string) (retNws []libnetwork.Network, err error) {
39
+	switch netType {
40
+	case "builtin":
41
+		for _, nw := range nws {
42
+			if runconfig.IsPreDefinedNetwork(nw.Name()) {
43
+				retNws = append(retNws, nw)
44
+			}
45
+		}
46
+	case "custom":
47
+		for _, nw := range nws {
48
+			if !runconfig.IsPreDefinedNetwork(nw.Name()) {
49
+				retNws = append(retNws, nw)
50
+			}
51
+		}
52
+	default:
53
+		return nil, fmt.Errorf("Invalid filter: 'type'='%s'", netType)
54
+	}
55
+	return retNws, nil
56
+}
57
+
58
+func filterNetworkByName(nws []libnetwork.Network, name string) (retNws []libnetwork.Network, err error) {
59
+	for _, nw := range nws {
60
+		// exact match (fast path)
61
+		if nw.Name() == name {
62
+			retNws = append(retNws, nw)
63
+			continue
64
+		}
65
+
66
+		// regexp match (slow path)
67
+		match, err := regexp.MatchString(name, nw.Name())
68
+		if err != nil || !match {
69
+			continue
70
+		} else {
71
+			retNws = append(retNws, nw)
72
+		}
73
+	}
74
+	return retNws, nil
75
+}
76
+
77
+func filterNetworkByID(nws []libnetwork.Network, id string) (retNws []libnetwork.Network, err error) {
78
+	for _, nw := range nws {
79
+		if strings.HasPrefix(nw.ID(), id) {
80
+			retNws = append(retNws, nw)
81
+		}
82
+	}
83
+	return retNws, nil
84
+}
85
+
86
+// FilterNetworks filters network list according to user specified filter
87
+// and returns user chosen networks
88
+func FilterNetworks(nws []libnetwork.Network, filter filters.Args) ([]libnetwork.Network, error) {
89
+	// if filter is empty, return original network list
90
+	if filter.Len() == 0 {
91
+		return nws, nil
92
+	}
93
+
94
+	var displayNet []libnetwork.Network
95
+	for fkey, fhandler := range supportedFilters {
96
+		errFilter := filter.WalkValues(fkey, func(fval string) error {
97
+			passList, err := fhandler(nws, fval)
98
+			if err != nil {
99
+				return err
100
+			}
101
+			displayNet = append(displayNet, passList...)
102
+			return nil
103
+		})
104
+		if errFilter != nil {
105
+			return nil, errFilter
106
+		}
107
+	}
108
+	return displayNet, nil
109
+}
... ...
@@ -37,6 +37,7 @@ import (
37 37
 	"github.com/docker/docker/pkg/signal"
38 38
 	"github.com/docker/docker/pkg/system"
39 39
 	"github.com/docker/docker/registry"
40
+	"github.com/docker/docker/runconfig"
40 41
 	"github.com/docker/docker/utils"
41 42
 	"github.com/docker/go-connections/tlsconfig"
42 43
 )
... ...
@@ -405,9 +406,11 @@ func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commo
405 405
 }
406 406
 
407 407
 func initRouter(s *apiserver.Server, d *daemon.Daemon) {
408
+	decoder := runconfig.ContainerDecoder{}
409
+
408 410
 	routers := []router.Router{
409
-		container.NewRouter(d),
410
-		image.NewRouter(d),
411
+		container.NewRouter(d, decoder),
412
+		image.NewRouter(d, decoder),
411 413
 		systemrouter.NewRouter(d),
412 414
 		volume.NewRouter(d),
413 415
 		build.NewRouter(dockerfile.NewBuildManager(d)),
... ...
@@ -296,8 +296,8 @@ func createNetwork(c *check.C, config types.NetworkCreate, shouldSucceed bool) s
296 296
 		return ""
297 297
 	}
298 298
 
299
-	c.Assert(status, checker.Equals, http.StatusCreated)
300 299
 	c.Assert(err, checker.IsNil)
300
+	c.Assert(status, checker.Equals, http.StatusCreated)
301 301
 
302 302
 	var nr types.NetworkCreateResponse
303 303
 	err = json.Unmarshal(resp, &nr)
... ...
@@ -10,6 +10,20 @@ import (
10 10
 	networktypes "github.com/docker/engine-api/types/network"
11 11
 )
12 12
 
13
+// ContainerDecoder implements httputils.ContainerDecoder
14
+// calling DecodeContainerConfig.
15
+type ContainerDecoder struct{}
16
+
17
+// DecodeConfig makes ContainerDecoder to implement httputils.ContainerDecoder
18
+func (r ContainerDecoder) DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
19
+	return DecodeContainerConfig(src)
20
+}
21
+
22
+// DecodeHostConfig makes ContainerDecoder to implement httputils.ContainerDecoder
23
+func (r ContainerDecoder) DecodeHostConfig(src io.Reader) (*container.HostConfig, error) {
24
+	return DecodeHostConfig(src)
25
+}
26
+
13 27
 // DecodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper
14 28
 // struct and returns both a Config and an HostConfig struct
15 29
 // Be aware this function is not checking whether the resulted structs are nil,