diff options
Diffstat (limited to 'cmd/podman/create.go')
-rw-r--r-- | cmd/podman/create.go | 251 |
1 files changed, 176 insertions, 75 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 8b64a1cb0..182eb1e56 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/signal" + "github.com/docker/go-connections/nat" "github.com/docker/go-units" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" @@ -80,10 +81,11 @@ type createConfig struct { DNSServers []string //dns Entrypoint string //entrypoint Env map[string]string //env - Expose []string //expose - GroupAdd []uint32 // group-add - Hostname string //hostname + ExposedPorts map[nat.Port]struct{} + GroupAdd []uint32 // group-add + Hostname string //hostname Image string + ImageID string Interactive bool //interactive IpcMode container.IpcMode //ipc IP6Address string //ipv6 @@ -99,7 +101,8 @@ type createConfig struct { NetworkAlias []string //network-alias PidMode container.PidMode //pid NsUser string - Pod string //pod + Pod string //pod + PortBindings nat.PortMap Privileged bool //privileged Publish []string //publish PublishAll bool //publish-all @@ -115,8 +118,7 @@ type createConfig struct { Sysctl map[string]string //sysctl Tmpfs []string // tmpfs Tty bool //tty - User uint32 //user - Group uint32 // group + User string //user UtsMode container.UTSMode //uts Volumes []string //volume WorkDir string //workdir @@ -148,7 +150,6 @@ var createCommand = cli.Command{ func createCmd(c *cli.Context) error { // TODO should allow user to create based off a directory on the host not just image // Need CLI support for this - var imageName string if err := validateFlags(c, createFlags); err != nil { return err } @@ -164,54 +165,19 @@ func createCmd(c *cli.Context) error { return err } - // Deal with the image after all the args have been checked - createImage := runtime.NewImage(createConfig.Image) - createImage.LocalName, _ = createImage.GetLocalImageName() - if createImage.LocalName == "" { - // The image wasnt found by the user input'd name or its fqname - // Pull the image - var writer io.Writer - if !createConfig.Quiet { - writer = os.Stdout - } - createImage.Pull(writer) - } - runtimeSpec, err := createConfigToOCISpec(createConfig) if err != nil { return err } - if createImage.LocalName != "" { - nameIsID, err := runtime.IsImageID(createImage.LocalName) - if err != nil { - return err - } - if nameIsID { - // If the input from the user is an ID, then we need to get the image - // name for cstorage - createImage.LocalName, err = createImage.GetNameByID() - if err != nil { - return err - } - } - imageName = createImage.LocalName - } else { - imageName, err = createImage.GetFQName() - } - if err != nil { - return err - } - imageID, err := createImage.GetImageID() - if err != nil { - return err - } options, err := createConfig.GetContainerCreateOptions() if err != nil { return errors.Wrapf(err, "unable to parse new container options") } // Gather up the options for NewContainer which consist of With... funcs - options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false)) + options = append(options, libpod.WithRootFSFromImage(createConfig.ImageID, createConfig.Image, true)) options = append(options, libpod.WithSELinuxLabels(createConfig.ProcessLabel, createConfig.MountLabel)) + options = append(options, libpod.WithLabels(createConfig.Labels)) + options = append(options, libpod.WithUser(createConfig.User)) options = append(options, libpod.WithShmDir(createConfig.ShmDir)) ctr, err := runtime.NewContainer(runtimeSpec, options...) if err != nil { @@ -300,13 +266,101 @@ func parseSecurityOpt(config *createConfig, securityOpts []string) error { return err } +func exposedPorts(c *cli.Context, imageExposedPorts map[string]struct{}) (map[nat.Port]struct{}, map[nat.Port][]nat.PortBinding, error) { + // TODO Handle exposed ports from image + // Currently ignoring imageExposedPorts + + ports, portBindings, err := nat.ParsePortSpecs(c.StringSlice("publish")) + if err != nil { + return nil, nil, err + } + + for _, e := range c.StringSlice("expose") { + // Merge in exposed ports to the map of published ports + if strings.Contains(e, ":") { + return nil, nil, fmt.Errorf("invalid port format for --expose: %s", e) + } + //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] + proto, port := nat.SplitProtoPort(e) + //parse the start and end port and create a sequence of ports to expose + //if expose a port, the start and end port are the same + start, end, err := nat.ParsePortRange(port) + if err != nil { + return nil, nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", e, err) + } + for i := start; i <= end; i++ { + p, err := nat.NewPort(proto, strconv.FormatUint(i, 10)) + if err != nil { + return nil, nil, err + } + if _, exists := ports[p]; !exists { + ports[p] = struct{}{} + } + } + } + return ports, portBindings, nil +} + +// imageData pulls down the image if not stored locally and extracts the +// default container runtime data out of it. imageData returns the data +// to the caller. Example Data: Entrypoint, Env, WorkingDir, Labels ... +func imageData(c *cli.Context, runtime *libpod.Runtime, image string) (string, string, *libpod.ImageData, error) { + var err error + // Deal with the image after all the args have been checked + createImage := runtime.NewImage(image) + createImage.LocalName, _ = createImage.GetLocalImageName() + if createImage.LocalName == "" { + // The image wasnt found by the user input'd name or its fqname + // Pull the image + var writer io.Writer + if !c.Bool("quiet") { + writer = os.Stdout + } + createImage.Pull(writer) + } + + var imageName string + if createImage.LocalName != "" { + nameIsID, err := runtime.IsImageID(createImage.LocalName) + if err != nil { + return "", "", nil, err + } + if nameIsID { + // If the input from the user is an ID, then we need to get the image + // name for cstorage + createImage.LocalName, err = createImage.GetNameByID() + if err != nil { + return "", "", nil, err + } + } + imageName = createImage.LocalName + } else { + imageName, err = createImage.GetFQName() + } + if err != nil { + return "", "", nil, err + } + imageID, err := createImage.GetImageID() + if err != nil { + return "", "", nil, err + } + storageImage, err := runtime.GetImage(image) + if err != nil { + return "", "", nil, errors.Wrapf(err, "error getting storage image %q", image) + } + data, err := runtime.GetImageInspectInfo(*storageImage) + if err != nil { + return "", "", nil, errors.Wrapf(err, "error parsing image data %q", image) + } + return imageName, imageID, data, err +} + // Parses CLI options related to container creation into a config which can be // parsed into an OCI runtime spec func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, error) { var command []string var memoryLimit, memoryReservation, memorySwap, memoryKernel int64 var blkioWeight uint16 - var uid, gid uint32 if len(c.Args()) < 1 { return nil, errors.Errorf("image name or ID is required") @@ -317,33 +371,14 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er command = c.Args()[1:] } - // LABEL VARIABLES - labels, err := getAllLabels(c.StringSlice("label-file"), c.StringSlice("labels")) - if err != nil { - return &createConfig{}, errors.Wrapf(err, "unable to process labels") - } - // ENVIRONMENT VARIABLES - env := defaultEnvVariables - if err := readKVStrings(env, c.StringSlice("env-file"), c.StringSlice("env")); err != nil { - return &createConfig{}, errors.Wrapf(err, "unable to process environment variables") - } - sysctl, err := convertStringSliceToMap(c.StringSlice("sysctl"), "=") if err != nil { - return &createConfig{}, errors.Wrapf(err, "sysctl values must be in the form of KEY=VALUE") + return nil, errors.Wrapf(err, "sysctl values must be in the form of KEY=VALUE") } groupAdd, err := stringSlicetoUint32Slice(c.StringSlice("group-add")) if err != nil { - return &createConfig{}, errors.Wrapf(err, "invalid value for groups provided") - } - - if c.String("user") != "" { - // TODO - // We need to mount the imagefs and get the uid/gid - // For now, user zeros - uid = 0 - gid = 0 + return nil, errors.Wrapf(err, "invalid value for groups provided") } if c.String("memory") != "" { @@ -417,14 +452,79 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er } shmDir = ctr.ShmDir() } - stopSignal := syscall.SIGTERM + + imageName, imageID, data, err := imageData(c, runtime, image) + if err != nil { + return nil, err + } + + // USER + user := c.String("user") + if user == "" { + user = data.Config.User + } + + // STOP SIGNAL + stopSignal := syscall.SIGINT + signalString := data.Config.StopSignal if c.IsSet("stop-signal") { - stopSignal, err = signal.ParseSignal(c.String("stop-signal")) + signalString = c.String("stop-signal") + } + if signalString != "" { + stopSignal, err = signal.ParseSignal(signalString) if err != nil { return nil, err } } + // ENVIRONMENT VARIABLES + env := defaultEnvVariables + for _, e := range data.Config.Env { + split := strings.SplitN(e, "=", 2) + if len(split) > 1 { + env[split[0]] = split[1] + } else { + env[split[0]] = "" + } + } + if err := readKVStrings(env, c.StringSlice("env-file"), c.StringSlice("env")); err != nil { + return nil, errors.Wrapf(err, "unable to process environment variables") + } + + // LABEL VARIABLES + labels, err := getAllLabels(c.StringSlice("label-file"), c.StringSlice("labels")) + if err != nil { + return nil, errors.Wrapf(err, "unable to process labels") + } + for key, val := range data.Config.Labels { + if _, ok := labels[key]; !ok { + labels[key] = val + } + } + + // WORKING DIRECTORY + workDir := c.String("workdir") + if workDir == "" { + workDir = data.Config.WorkingDir + } + + // COMMAND + if len(command) == 0 { + command = data.Config.Cmd + } + + // ENTRYPOINT + entrypoint := c.String("entrypoint") + if entrypoint == "" { + entrypoint = strings.Join(data.Config.Entrypoint, " ") + } + + // EXPOSED PORTS + ports, portBindings, err := exposedPorts(c, data.Config.ExposedPorts) + if err != nil { + return nil, err + } + config := &createConfig{ Runtime: runtime, CapAdd: c.StringSlice("cap-add"), @@ -436,12 +536,13 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er DNSOpt: c.StringSlice("dns-opt"), DNSSearch: c.StringSlice("dns-search"), DNSServers: c.StringSlice("dns"), - Entrypoint: c.String("entrypoint"), + Entrypoint: entrypoint, Env: env, - Expose: c.StringSlice("expose"), + ExposedPorts: ports, GroupAdd: groupAdd, Hostname: c.String("hostname"), - Image: image, + Image: imageName, + ImageID: imageID, Interactive: c.Bool("interactive"), IP6Address: c.String("ipv6"), IPAddress: c.String("ip"), @@ -461,6 +562,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er Privileged: c.Bool("privileged"), Publish: c.StringSlice("publish"), PublishAll: c.Bool("publish-all"), + PortBindings: portBindings, Quiet: c.Bool("quiet"), ReadOnlyRootfs: c.Bool("read-only"), Resources: createResourceConfig{ @@ -499,10 +601,9 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er Sysctl: sysctl, Tmpfs: c.StringSlice("tmpfs"), Tty: tty, - User: uid, - Group: gid, + User: user, Volumes: c.StringSlice("volume"), - WorkDir: c.String("workdir"), + WorkDir: workDir, } if !config.Privileged { |