blob: 7445ce6320bb48cb1e2e2e01dacf49fb81a32199 [file] [edit]
package runconfig
import (
"encoding/json"
"io"
"runtime"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/v2/pkg/sysinfo"
)
// DecodeCreateRequest decodes a json encoded [container.CreateRequest] struct
// and performs some validation. Certain parameters need daemon-side validation
// that cannot be done on the client, as only the daemon knows what is valid
// for the platform.
func DecodeCreateRequest(src io.Reader, si *sysinfo.SysInfo) (container.CreateRequest, error) {
w, err := decodeCreateRequest(src)
if err != nil {
return container.CreateRequest{}, err
}
if err := validateCreateRequest(w, si); err != nil {
return container.CreateRequest{}, err
}
return w, nil
}
// decodeCreateRequest decodes a json encoded [container.CreateRequest] struct
// and sets some defaults.
func decodeCreateRequest(src io.Reader) (container.CreateRequest, error) {
// TODO(thaJeztah): replace with httputils.ReadJSON ?
var w container.CreateRequest
if err := loadJSON(src, &w); err != nil {
return container.CreateRequest{}, err
}
if w.Config == nil {
return container.CreateRequest{}, validationError("config cannot be empty in order to create a container")
}
if w.Config.Volumes == nil {
w.Config.Volumes = make(map[string]struct{})
}
if w.HostConfig == nil {
w.HostConfig = &container.HostConfig{}
}
if w.HostConfig.PortBindings == nil {
w.HostConfig.PortBindings = make(network.PortMap)
}
// Make sure NetworkMode has an acceptable value. We do this to ensure
// backwards compatible API behavior.
//
// TODO(thaJeztah): platform check may be redundant, as other code-paths execute this unconditionally. Also check if this code is still needed here, or already handled elsewhere.
if runtime.GOOS != "windows" && w.HostConfig.NetworkMode == "" {
w.HostConfig.NetworkMode = network.NetworkDefault
}
if w.NetworkingConfig == nil {
w.NetworkingConfig = &network.NetworkingConfig{}
}
if w.NetworkingConfig.EndpointsConfig == nil {
w.NetworkingConfig.EndpointsConfig = make(map[string]*network.EndpointSettings)
}
return w, nil
}
func validateCreateRequest(w container.CreateRequest, si *sysinfo.SysInfo) error {
if err := validateNetMode(w.Config, w.HostConfig); err != nil {
return err
}
if err := validateIsolation(w.HostConfig); err != nil {
return err
}
if err := validateQoS(w.HostConfig); err != nil {
return err
}
if err := validateResources(w.HostConfig, si); err != nil {
return err
}
if err := validatePrivileged(w.HostConfig); err != nil {
return err
}
if err := validateReadonlyRootfs(w.HostConfig); err != nil {
return err
}
return nil
}
// loadJSON is similar to api/server/httputils.ReadJSON()
func loadJSON(src io.Reader, out any) error {
dec := json.NewDecoder(src)
if err := dec.Decode(&out); err != nil {
// invalidJSONError allows unwrapping the error to detect io.EOF etc.
return invalidJSONError{error: err}
}
if dec.More() {
return validationError("unexpected content after JSON")
}
return nil
}