Move the Config portion of Inspect into libpod

While we're at it, rewrite how we populate it. There were several
potential segfaults in the optional spec.Process block, and a few
fields not being populated correctly versus 'docker inspect'.

Signed-off-by: Matthew Heon <mheon@redhat.com>
This commit is contained in:
Matthew Heon 2019-06-17 15:09:24 -04:00
parent 54b9c95005
commit 33b71944c0
2 changed files with 108 additions and 44 deletions

View file

@ -1,9 +1,6 @@
package shared
import (
"strings"
"github.com/containers/image/manifest"
"github.com/containers/libpod/libpod"
cc "github.com/containers/libpod/pkg/spec"
"github.com/docker/go-connections/nat"
@ -17,7 +14,6 @@ import (
type InspectContainer struct {
*libpod.InspectContainerData
HostConfig *InspectContainerHostConfig `json:"HostConfig"`
Config *InspectContainerConfig `json:"Config"`
}
// InspectContainerHostConfig holds Container configuration that is not specific
@ -82,31 +78,6 @@ type InspectContainerHostConfig struct {
Tmpfs []string `json:"Tmpfs"`
}
// InspectContainerConfig holds further data about a container, again mostly
// not directly stored in Libpod. This struct is matched to the output of
// `docker inspect`.
type InspectContainerConfig struct {
Hostname string `json:"Hostname"`
DomainName string `json:"Domainname"` //TODO
User specs.User `json:"User"`
AttachStdin bool `json:"AttachStdin"` //TODO
AttachStdout bool `json:"AttachStdout"` //TODO
AttachStderr bool `json:"AttachStderr"` //TODO
Tty bool `json:"Tty"`
OpenStdin bool `json:"OpenStdin"`
StdinOnce bool `json:"StdinOnce"` //TODO
Env []string `json:"Env"`
Cmd []string `json:"Cmd"`
Image string `json:"Image"`
Volumes map[string]struct{} `json:"Volumes"`
WorkingDir string `json:"WorkingDir"`
Entrypoint string `json:"Entrypoint"`
Labels map[string]string `json:"Labels"`
Annotations map[string]string `json:"Annotations"`
StopSignal uint `json:"StopSignal"`
Healthcheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
}
// InspectLogConfig holds information about a container's configured log driver
// and is presently unused. It is retained for Docker compatability.
type InspectLogConfig struct {
@ -181,21 +152,6 @@ func GetCtrInspectInfo(config *libpod.ContainerConfig, ctrInspectData *libpod.In
SecurityOpt: createArtifact.SecurityOpts,
Tmpfs: createArtifact.Tmpfs,
},
&InspectContainerConfig{
Hostname: spec.Hostname,
User: spec.Process.User,
Env: spec.Process.Env,
Image: config.RootfsImageName,
WorkingDir: spec.Process.Cwd,
Labels: config.Labels,
Annotations: spec.Annotations,
Tty: spec.Process.Terminal,
OpenStdin: config.Stdin,
StopSignal: config.StopSignal,
Cmd: config.Spec.Process.Args,
Entrypoint: strings.Join(createArtifact.Entrypoint, " "),
Healthcheck: config.HealthCheckConfig,
},
}
return data, nil
}

View file

@ -1,8 +1,10 @@
package libpod
import (
"strings"
"time"
"github.com/containers/image/manifest"
"github.com/containers/libpod/libpod/driver"
"github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
@ -49,6 +51,53 @@ type InspectContainerData struct {
ExitCommand []string `json:"ExitCommand"`
Namespace string `json:"Namespace"`
IsInfra bool `json:"IsInfra"`
Config *InspectContainerConfig `json:"Config"`
}
// InspectContainerConfig holds further data about how a container was initially
// configured.
type InspectContainerConfig struct {
// Container hostname
Hostname string `json:"Hostname"`
// Container domain name - unused at present
DomainName string `json:"Domainname"`
// User the container was launched with
User string `json:"User"`
// Unused, at present
AttachStdin bool `json:"AttachStdin"`
// Unused, at present
AttachStdout bool `json:"AttachStdout"`
// Unused, at present
AttachStderr bool `json:"AttachStderr"`
// Whether the container creates a TTY
Tty bool `json:"Tty"`
// Whether the container leaves STDIN open
OpenStdin bool `json:"OpenStdin"`
// Whether STDIN is only left open once.
// Presently not supported by Podman, unused.
StdinOnce bool `json:"StdinOnce"`
// Container environment variables
Env []string `json:"Env"`
// Container command
Cmd []string `json:"Cmd"`
// Container image
Image string `json:"Image"`
// Unused, at present. I've never seen this field populated.
Volumes map[string]struct{} `json:"Volumes"`
// Container working directory
WorkingDir string `json:"WorkingDir"`
// Container entrypoint
Entrypoint string `json:"Entrypoint"`
// On-build arguments - presently unused. More of Buildah's domain.
OnBuild *string `json:"OnBuild"`
// Container labels
Labels map[string]string `json:"Labels"`
// Container annotations
Annotations map[string]string `json:"Annotations"`
// Container stop signal
StopSignal uint `json:"StopSignal"`
// Configured healthcheck for the container
Healthcheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
}
// InspectMount provides a record of a single mount in a container. It contains
@ -284,6 +333,12 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
// Get information on the container's network namespace (if present)
data = c.getContainerNetworkInfo(data)
inspectConfig, err := c.generateInspectContainerConfig(spec)
if err != nil {
return nil, err
}
data.Config = inspectConfig
if size {
rootFsSize, err := c.rootFsSize()
if err != nil {
@ -401,3 +456,56 @@ func parseMountOptionsForInspect(options []string, mount *InspectMount) {
mount.Mode = zZ
mount.Options = otherOpts
}
// Generate the InspectContainerConfig struct for the Config field of Inspect.
func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*InspectContainerConfig, error) {
ctrConfig := new(InspectContainerConfig)
ctrConfig.Hostname = c.Hostname()
ctrConfig.User = c.config.User
if spec.Process != nil {
ctrConfig.Tty = spec.Process.Terminal
ctrConfig.Env = []string{}
for _, val := range spec.Process.Env {
ctrConfig.Env = append(ctrConfig.Env, val)
}
ctrConfig.WorkingDir = spec.Process.Cwd
}
ctrConfig.OpenStdin = c.config.Stdin
ctrConfig.Image = c.config.RootfsImageName
// Leave empty is not explicitly overwritten by user
if len(c.config.Command) != 0 {
ctrConfig.Cmd = []string{}
for _, val := range c.config.Command {
ctrConfig.Cmd = append(ctrConfig.Cmd, val)
}
}
// Leave empty if not explicitly overwritten by user
if len(c.config.Entrypoint) != 0 {
ctrConfig.Entrypoint = strings.Join(c.config.Entrypoint, " ")
}
if len(c.config.Labels) != 0 {
ctrConfig.Labels = make(map[string]string)
for k, v := range c.config.Labels {
ctrConfig.Labels[k] = v
}
}
if len(spec.Annotations) != 0 {
ctrConfig.Annotations = make(map[string]string)
for k, v := range spec.Annotations {
ctrConfig.Annotations[k] = v
}
}
ctrConfig.StopSignal = c.config.StopSignal
// TODO: should JSON deep copy this to ensure internal pointers don't
// leak.
ctrConfig.Healthcheck = c.config.HealthCheckConfig
return ctrConfig, nil
}