func setEnvFromImageConfig(config, imageConfig *containertypes.Config) { // Set PATH in ENV if needed setPathFromImageConfig(config, imageConfig) containerEnv := make(map[string]string, len(config.Env)) for _, env := range config.Env { kv := strings.SplitN(env, "=", 2) var val string if len(kv) == 2 { val = kv[1] } containerEnv[kv[0]] = val } // Set TERM to xterm if tty is set, unless user supplied a different TERM if config.Tty { if _, ok := containerEnv["TERM"]; !ok { config.Env = append(config.Env, "TERM=xterm") } } // add remaining environment variables from the image config to the container // config, taking care not to overwrite anything for _, imageEnv := range imageConfig.Env { key := strings.SplitN(imageEnv, "=", 2)[0] // is environment variable already set in container config? if _, ok := containerEnv[key]; !ok { // no? let's copy it from the image config config.Env = append(config.Env, imageEnv) } } }
// SetConfigOptions is a place to add necessary container configuration // values that were not explicitly supplied by the user func setCreateConfigOptions(config, imageConfig *containertypes.Config) { // Overwrite or append the image's config from the CLI with the metadata from the image's // layer metadata where appropriate if len(config.Cmd) == 0 { config.Cmd = imageConfig.Cmd } if config.WorkingDir == "" { config.WorkingDir = imageConfig.WorkingDir } if len(config.Entrypoint) == 0 { config.Entrypoint = imageConfig.Entrypoint } if config.Volumes == nil { config.Volumes = imageConfig.Volumes } else { for k, v := range imageConfig.Volumes { //NOTE: the value of the map is an empty struct. // we also do not care about duplicates. // This Volumes map is really a Set. config.Volumes[k] = v } } // set up environment setEnvFromImageConfig(config, imageConfig) }
func (m *UpdateServiceSnapshotByContainer) Process() error { _, imageName := m.info.GetName() cached, err := utils.IsImageCached(imageName) if err != nil { fmt.Println(err) return err } if !cached { //TODO: remove this image to save server disk err := utils.PullImage(imageName) if err != nil { return err } } var config container.Config config.Image = imageName config.Cmd = []string{snapshotProcess, m.info.CallbackID, m.info.Host, m.info.DataProto} var hostConfig container.HostConfig hostConfig.Binds = append(hostConfig.Binds, fmt.Sprintf("%s:%s", m.info.DataURL, snapshotMountDir)) containerName := "scan-" + m.info.CallbackID err = utils.StartContainer(config, hostConfig, containerName) return err }
func TestUtilsContainer(t *testing.T) { //TODO: dockyard dev team should provide small testing containers. imageName := "google/nodejs" containerName := "" cached, err := utils.IsImageCached(imageName) if err == utils.ErrorsNoDockerClient { fmt.Println("Please start a docker daemon to continue the container operation test") return } assert.Nil(t, err, "Fail to load Image") if !cached { err := utils.PullImage(imageName) assert.Nil(t, err, "Fail to pull image") } tmpFile, err := ioutil.TempFile("/tmp", "dockyard-test-container-oper") assert.Nil(t, err, "System err, fail to create temp file") defer os.Remove(tmpFile.Name()) var config container.Config config.Image = imageName config.Cmd = []string{"touch", tmpFile.Name()} var hostConfig container.HostConfig hostConfig.Binds = append(hostConfig.Binds, "/tmp:/tmp") utils.StartContainer(config, hostConfig, containerName) //TODO: stop, remove the container process assert.Equal(t, true, utils.IsFileExist(tmpFile.Name()), "Fail to touch file by using StartContainer") }
// modifyContainerConfig applies container security context config to dockercontainer.Config. func modifyContainerConfig(sc *runtimeapi.LinuxContainerSecurityContext, config *dockercontainer.Config) { if sc == nil { return } if sc.RunAsUser != nil { config.User = strconv.FormatInt(sc.GetRunAsUser(), 10) } if sc.RunAsUsername != nil { config.User = sc.GetRunAsUsername() } }
// ModifyContainerConfig is called before the Docker createContainer call. // The security context provider can make changes to the Config with which // the container is created. func (p SimpleSecurityContextProvider) ModifyContainerConfig(pod *api.Pod, container *api.Container, config *dockercontainer.Config) { effectiveSC := DetermineEffectiveSecurityContext(pod, container) if effectiveSC == nil { return } if effectiveSC.RunAsUser != nil { config.User = strconv.Itoa(int(*effectiveSC.RunAsUser)) } }
// 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) }
func setPathFromImageConfig(config, imageConfig *containertypes.Config) { // check if user supplied PATH environment variable at creation time for _, v := range config.Env { if strings.HasPrefix(v, "PATH=") { // a PATH is set, bail return } } // check to see if the image this container is created from supplies a PATH for _, v := range imageConfig.Env { if strings.HasPrefix(v, "PATH=") { // a PATH was found, add it to the config config.Env = append(config.Env, v) return } } // no PATH set, use the default config.Env = append(config.Env, fmt.Sprintf("PATH=%s", defaultEnvPath)) }
func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error { if img != nil && img.Config != nil { if err := merge(config, img.Config); err != nil { return err } } // Reset the Entrypoint if it is [""] if len(config.Entrypoint) == 1 && config.Entrypoint[0] == "" { config.Entrypoint = nil } if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { return fmt.Errorf("No command specified") } return 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) }
func (daemon *Daemon) generateHostname(id string, config *containertypes.Config) { // Generate default hostname if config.Hostname == "" { config.Hostname = id[:12] } }
func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) { var containerIDFile *cidFile if cidfile != "" { var err error if containerIDFile, err = newCIDFile(cidfile); err != nil { return nil, err } defer containerIDFile.Close() } ref, err := reference.ParseNamed(config.Image) if err != nil { return nil, err } ref = reference.WithDefaultTag(ref) var trustedRef reference.Canonical if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() { var err error trustedRef, err = cli.trustedReference(ref) if err != nil { return nil, err } config.Image = trustedRef.String() } //create the container response, err := cli.client.ContainerCreate(config, hostConfig, networkingConfig, name) //if image not found try to pull it if err != nil { if client.IsErrImageNotFound(err) { fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String()) // we don't want to write to stdout anything apart from container.ID if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { return nil, err } if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil { if err := cli.tagTrusted(trustedRef, ref); err != nil { return nil, err } } // Retry var retryErr error response, retryErr = cli.client.ContainerCreate(config, hostConfig, networkingConfig, name) if retryErr != nil { return nil, retryErr } } else { return nil, err } } for _, warning := range response.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } if containerIDFile != nil { if err = containerIDFile.Write(response.ID); err != nil { return nil, err } } return &response, nil }
// merge merges two Config, the image container configuration (defaults values), // and the user container configuration, either passed by the API or generated // by the cli. // It will mutate the specified user configuration (userConf) with the image // configuration where the user configuration is incomplete. func merge(userConf, imageConf *containertypes.Config) error { if userConf.User == "" { userConf.User = imageConf.User } if len(userConf.ExposedPorts) == 0 { userConf.ExposedPorts = imageConf.ExposedPorts } else if imageConf.ExposedPorts != nil { if userConf.ExposedPorts == nil { userConf.ExposedPorts = make(nat.PortSet) } for port := range imageConf.ExposedPorts { if _, exists := userConf.ExposedPorts[port]; !exists { userConf.ExposedPorts[port] = struct{}{} } } } if len(userConf.Env) == 0 { userConf.Env = imageConf.Env } else { for _, imageEnv := range imageConf.Env { found := false imageEnvKey := strings.Split(imageEnv, "=")[0] for _, userEnv := range userConf.Env { userEnvKey := strings.Split(userEnv, "=")[0] if imageEnvKey == userEnvKey { found = true break } } if !found { userConf.Env = append(userConf.Env, imageEnv) } } } if userConf.Labels == nil { userConf.Labels = map[string]string{} } if imageConf.Labels != nil { for l := range userConf.Labels { imageConf.Labels[l] = userConf.Labels[l] } userConf.Labels = imageConf.Labels } if userConf.Entrypoint.Len() == 0 { if userConf.Cmd.Len() == 0 { userConf.Cmd = imageConf.Cmd } if userConf.Entrypoint == nil { userConf.Entrypoint = imageConf.Entrypoint } } if userConf.WorkingDir == "" { userConf.WorkingDir = imageConf.WorkingDir } if len(userConf.Volumes) == 0 { userConf.Volumes = imageConf.Volumes } else { for k, v := range imageConf.Volumes { userConf.Volumes[k] = v } } return nil }
// merge merges two Config, the image container configuration (defaults values), // and the user container configuration, either passed by the API or generated // by the cli. // It will mutate the specified user configuration (userConf) with the image // configuration where the user configuration is incomplete. func merge(userConf, imageConf *containertypes.Config) error { if userConf.User == "" { userConf.User = imageConf.User } if len(userConf.ExposedPorts) == 0 { userConf.ExposedPorts = imageConf.ExposedPorts } else if imageConf.ExposedPorts != nil { if userConf.ExposedPorts == nil { userConf.ExposedPorts = make(nat.PortSet) } for port := range imageConf.ExposedPorts { if _, exists := userConf.ExposedPorts[port]; !exists { userConf.ExposedPorts[port] = struct{}{} } } } if len(userConf.Env) == 0 { userConf.Env = imageConf.Env } else { for _, imageEnv := range imageConf.Env { found := false imageEnvKey := strings.Split(imageEnv, "=")[0] for _, userEnv := range userConf.Env { userEnvKey := strings.Split(userEnv, "=")[0] if imageEnvKey == userEnvKey { found = true break } } if !found { userConf.Env = append(userConf.Env, imageEnv) } } } if userConf.Labels == nil { userConf.Labels = map[string]string{} } if imageConf.Labels != nil { for l := range userConf.Labels { imageConf.Labels[l] = userConf.Labels[l] } userConf.Labels = imageConf.Labels } if len(userConf.Entrypoint) == 0 { if len(userConf.Cmd) == 0 { userConf.Cmd = imageConf.Cmd userConf.ArgsEscaped = imageConf.ArgsEscaped } if userConf.Entrypoint == nil { userConf.Entrypoint = imageConf.Entrypoint } } if imageConf.Healthcheck != nil { if userConf.Healthcheck == nil { userConf.Healthcheck = imageConf.Healthcheck } else { if len(userConf.Healthcheck.Test) == 0 { userConf.Healthcheck.Test = imageConf.Healthcheck.Test } if userConf.Healthcheck.Interval == 0 { userConf.Healthcheck.Interval = imageConf.Healthcheck.Interval } if userConf.Healthcheck.Timeout == 0 { userConf.Healthcheck.Timeout = imageConf.Healthcheck.Timeout } if userConf.Healthcheck.Retries == 0 { userConf.Healthcheck.Retries = imageConf.Healthcheck.Retries } } } if userConf.WorkingDir == "" { userConf.WorkingDir = imageConf.WorkingDir } if len(userConf.Volumes) == 0 { userConf.Volumes = imageConf.Volumes } else { for k, v := range imageConf.Volumes { userConf.Volumes[k] = v } } if userConf.StopSignal == "" { userConf.StopSignal = imageConf.StopSignal } return nil }
// modifyContainerConfig applies container security context config to dockercontainer.Config. func modifyContainerConfig(sc *runtimeapi.LinuxContainerSecurityContext, config *dockercontainer.Config) { config.User = sc.GetRunAsUser() }
// BuildContainerConfig creates a cluster.ContainerConfig from a Config, HostConfig, and NetworkingConfig func BuildContainerConfig(c container.Config, h container.HostConfig, n network.NetworkingConfig) *ContainerConfig { var ( affinities []string constraints []string reschedulePolicies []string env []string ) // only for tests if c.Labels == nil { c.Labels = make(map[string]string) } // parse affinities from labels (ex. docker run --label 'com.docker.swarm.affinities=["container==redis","image==nginx"]') if labels, ok := c.Labels[SwarmLabelNamespace+".affinities"]; ok { json.Unmarshal([]byte(labels), &affinities) } // parse constraints from labels (ex. docker run --label 'com.docker.swarm.constraints=["region==us-east","storage==ssd"]') if labels, ok := c.Labels[SwarmLabelNamespace+".constraints"]; ok { json.Unmarshal([]byte(labels), &constraints) } // parse reschedule policy from labels (ex. docker run --label 'com.docker.swarm.reschedule-policies=["on-node-failure"]') if labels, ok := c.Labels[SwarmLabelNamespace+".reschedule-policies"]; ok { json.Unmarshal([]byte(labels), &reschedulePolicies) } // parse affinities/constraints/reschedule policies from env (ex. docker run -e affinity:container==redis -e affinity:image==nginx -e constraint:region==us-east -e constraint:storage==ssd -e reschedule:off) for _, e := range c.Env { if ok, key, value := parseEnv(e); ok && key == "affinity" { affinities = append(affinities, value) } else if ok && key == "constraint" { constraints = append(constraints, value) } else if ok && key == "reschedule" { reschedulePolicies = append(reschedulePolicies, value) } else { env = append(env, e) } } // remove affinities/constraints/reschedule policies from env c.Env = env // store affinities in labels if len(affinities) > 0 { if labels, err := json.Marshal(affinities); err == nil { c.Labels[SwarmLabelNamespace+".affinities"] = string(labels) } } // store constraints in labels if len(constraints) > 0 { if labels, err := json.Marshal(constraints); err == nil { c.Labels[SwarmLabelNamespace+".constraints"] = string(labels) } } // store reschedule policies in labels if len(reschedulePolicies) > 0 { if labels, err := json.Marshal(reschedulePolicies); err == nil { c.Labels[SwarmLabelNamespace+".reschedule-policies"] = string(labels) } } return &ContainerConfig{c, h, n} }