// verifyContainerSettings performs validation of the hostconfig and config // structures. func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool, validateHostname bool) ([]string, error) { // First perform verification of settings common across all platforms. if config != nil { if config.WorkingDir != "" { config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics if !system.IsAbs(config.WorkingDir) { return nil, fmt.Errorf("the working directory '%s' is invalid, it needs to be an absolute path", config.WorkingDir) } } if len(config.StopSignal) > 0 { _, err := signal.ParseSignal(config.StopSignal) if err != nil { return nil, err } } // Validate if the given hostname is RFC 1123 (https://tools.ietf.org/html/rfc1123) compliant. if validateHostname && len(config.Hostname) > 0 { // RFC1123 specifies that 63 bytes is the maximium length // Windows has the limitation of 63 bytes in length // Linux hostname is limited to HOST_NAME_MAX=64, not including the terminating null byte. // We limit the length to 63 bytes here to match RFC1035 and RFC1123. matched, _ := regexp.MatchString("^(([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])\\.)*([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])$", config.Hostname) if len(config.Hostname) > 63 || !matched { return nil, fmt.Errorf("invalid hostname format: %s", config.Hostname) } } } if hostConfig == nil { return nil, nil } if hostConfig.AutoRemove && !hostConfig.RestartPolicy.IsNone() { return nil, fmt.Errorf("can't create 'AutoRemove' container with restart policy") } for port := range hostConfig.PortBindings { _, portStr := nat.SplitProtoPort(string(port)) if _, err := nat.ParsePort(portStr); err != nil { return nil, fmt.Errorf("invalid port specification: %q", portStr) } for _, pb := range hostConfig.PortBindings[port] { _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) if err != nil { return nil, fmt.Errorf("invalid port specification: %q", pb.HostPort) } } } // Now do platform-specific verification return verifyPlatformContainerSettings(daemon, hostConfig, config, update) }
// FIXME(vdemeester) port to opts.PortOpt // This validation is only used for `--publish-rm`. // The `--publish-rm` takes: // <TargetPort>[/<Protocol>] (e.g., 80, 80/tcp, 53/udp) func validatePublishRemove(val string) (string, error) { proto, port := nat.SplitProtoPort(val) if proto != "tcp" && proto != "udp" { return "", fmt.Errorf("invalid protocol '%s' for %s", proto, val) } if strings.Contains(port, ":") { return "", fmt.Errorf("invalid port format: '%s', should be <TargetPort>[/<Protocol>] (e.g., 80, 80/tcp, 53/udp)", port) } if _, err := nat.ParsePort(port); err != nil { return "", err } return val, nil }
// verifyContainerSettings performs validation of the hostconfig and config // structures. func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { // First perform verification of settings common across all platforms. if config != nil { if config.WorkingDir != "" { config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics if !system.IsAbs(config.WorkingDir) { return nil, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path", config.WorkingDir) } } if len(config.StopSignal) > 0 { _, err := signal.ParseSignal(config.StopSignal) if err != nil { return nil, err } } } if hostConfig == nil { return nil, nil } logCfg := daemon.getLogConfig(hostConfig.LogConfig) if err := logger.ValidateLogOpts(logCfg.Type, logCfg.Config); err != nil { return nil, err } for port := range hostConfig.PortBindings { _, portStr := nat.SplitProtoPort(string(port)) if _, err := nat.ParsePort(portStr); err != nil { return nil, fmt.Errorf("Invalid port specification: %q", portStr) } for _, pb := range hostConfig.PortBindings[port] { _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) if err != nil { return nil, fmt.Errorf("Invalid port specification: %q", pb.HostPort) } } } // Now do platform-specific verification return verifyPlatformContainerSettings(daemon, hostConfig, config, update) }
// transformContainer generates the container type expected by the docker ps command. func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) { newC := &types.Container{ ID: container.ID, Names: ctx.names[container.ID], ImageID: container.ImageID.String(), } if newC.Names == nil { // Dead containers will often have no name, so make sure the response isn't null newC.Names = []string{} } image := container.Config.Image // if possible keep the original ref if image != container.ImageID.String() { id, err := daemon.GetImageID(image) if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE { return nil, err } if err != nil || id != container.ImageID { image = container.ImageID.String() } } newC.Image = image if len(container.Args) > 0 { args := []string{} for _, arg := range container.Args { if strings.Contains(arg, " ") { args = append(args, fmt.Sprintf("'%s'", arg)) } else { args = append(args, arg) } } argsAsString := strings.Join(args, " ") newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString) } else { newC.Command = container.Path } newC.Created = container.Created.Unix() newC.State = container.State.StateString() newC.Status = container.State.String() newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode) // copy networks to avoid races networks := make(map[string]*networktypes.EndpointSettings) for name, network := range container.NetworkSettings.Networks { if network == nil { continue } networks[name] = &networktypes.EndpointSettings{ EndpointID: network.EndpointID, Gateway: network.Gateway, IPAddress: network.IPAddress, IPPrefixLen: network.IPPrefixLen, IPv6Gateway: network.IPv6Gateway, GlobalIPv6Address: network.GlobalIPv6Address, GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen, MacAddress: network.MacAddress, } if network.IPAMConfig != nil { networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{ IPv4Address: network.IPAMConfig.IPv4Address, IPv6Address: network.IPAMConfig.IPv6Address, } } } newC.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks} newC.Ports = []types.Port{} for port, bindings := range container.NetworkSettings.Ports { p, err := nat.ParsePort(port.Port()) if err != nil { return nil, err } if len(bindings) == 0 { newC.Ports = append(newC.Ports, types.Port{ PrivatePort: p, Type: port.Proto(), }) continue } for _, binding := range bindings { h, err := nat.ParsePort(binding.HostPort) if err != nil { return nil, err } newC.Ports = append(newC.Ports, types.Port{ PrivatePort: p, PublicPort: h, Type: port.Proto(), IP: binding.HostIP, }) } } if ctx.Size { sizeRw, sizeRootFs := daemon.getSize(container) newC.SizeRw = sizeRw newC.SizeRootFs = sizeRootFs } newC.Labels = container.Config.Labels return newC, nil }
// verifyContainerSettings performs validation of the hostconfig and config // structures. func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { // First perform verification of settings common across all platforms. if config != nil { if config.WorkingDir != "" { config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics if !system.IsAbs(config.WorkingDir) { return nil, fmt.Errorf("the working directory '%s' is invalid, it needs to be an absolute path", config.WorkingDir) } } if len(config.StopSignal) > 0 { _, err := signal.ParseSignal(config.StopSignal) if err != nil { return nil, err } } // Validate if Env contains empty variable or not (e.g., ``, `=foo`) for _, env := range config.Env { if _, err := opts.ValidateEnv(env); err != nil { return nil, err } } } if hostConfig == nil { return nil, nil } if hostConfig.AutoRemove && !hostConfig.RestartPolicy.IsNone() { return nil, fmt.Errorf("can't create 'AutoRemove' container with restart policy") } for port := range hostConfig.PortBindings { _, portStr := nat.SplitProtoPort(string(port)) if _, err := nat.ParsePort(portStr); err != nil { return nil, fmt.Errorf("invalid port specification: %q", portStr) } for _, pb := range hostConfig.PortBindings[port] { _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) if err != nil { return nil, fmt.Errorf("invalid port specification: %q", pb.HostPort) } } } p := hostConfig.RestartPolicy switch p.Name { case "always", "unless-stopped", "no": if p.MaximumRetryCount != 0 { return nil, fmt.Errorf("maximum retry count cannot be used with restart policy '%s'", p.Name) } case "on-failure": if p.MaximumRetryCount < 0 { return nil, fmt.Errorf("maximum retry count cannot be negative") } case "": // do nothing default: return nil, fmt.Errorf("invalid restart policy '%s'", p.Name) } // Now do platform-specific verification return verifyPlatformContainerSettings(daemon, hostConfig, config, update) }