func (s *IntegrationTestSuite) TestLongContainerName(c *chk.C) { id, err := containers.NewIdentifier("IntTest006xxxxxxxxxxxxxx") c.Assert(err, chk.IsNil) s.containerIds = append(s.containerIds, id) hostContainerId := fmt.Sprintf("%v/%v", s.daemonURI, id) cmd := exec.Command("/usr/bin/gear", "install", TestImage, hostContainerId, "--start", "--ports=8080:4003", "--isolate") data, err := cmd.CombinedOutput() c.Log(string(data)) c.Assert(err, chk.IsNil) s.assertContainerState(c, id, CONTAINER_STARTED) s.assertFilePresent(c, id.UnitPathFor(), 0664, true) s.assertFilePresent(c, filepath.Join(id.HomePath(), "container-init.sh"), 0700, false) ports, err := containers.GetExistingPorts(id) c.Assert(err, chk.IsNil) c.Assert(len(ports), chk.Equals, 1) t := time.NewTicker(time.Second / 10) defer t.Stop() select { case <-t.C: resp, err := http.Get(fmt.Sprintf("http://0.0.0.0:%v", ports[0].External)) if err == nil { c.Assert(resp.StatusCode, chk.Equals, 200) } case <-time.After(time.Second * 15): c.Fail() } }
func (s *IntegrationTestSuite) TestStartStopContainer(c *chk.C) { id, err := containers.NewIdentifier("IntTest003") c.Assert(err, chk.IsNil) s.containerIds = append(s.containerIds, id) hostContainerId := fmt.Sprintf("%v/%v", s.daemonURI, id) cmd := exec.Command("/usr/bin/gear", "install", TestImage, hostContainerId, "--ports=8080:4001", "--isolate") data, err := cmd.CombinedOutput() c.Log(string(data)) c.Assert(err, chk.IsNil) s.assertFilePresent(c, id.UnitPathFor(), 0664, true) cmd = exec.Command("/usr/bin/gear", "start", hostContainerId) data, err = cmd.CombinedOutput() c.Log(string(data)) c.Assert(err, chk.IsNil) s.assertContainerState(c, id, CONTAINER_STARTED) s.assertFilePresent(c, filepath.Join(id.HomePath(), "container-init.sh"), 0700, false) ports, err := containers.GetExistingPorts(id) c.Assert(err, chk.IsNil) c.Assert(len(ports), chk.Equals, 1) resp, err := http.Get(fmt.Sprintf("http://0.0.0.0:%v", ports[0].External)) c.Assert(err, chk.IsNil) c.Assert(resp.StatusCode, chk.Equals, 200) cmd = exec.Command("/usr/bin/gear", "stop", hostContainerId) data, err = cmd.CombinedOutput() c.Log(string(data)) c.Assert(err, chk.IsNil) s.assertContainerState(c, id, CONTAINER_STOPPED) }
func (s *IntegrationTestSuite) TestSimpleInstallAndStartImage(c *chk.C) { id, err := containers.NewIdentifier("IntTest000") c.Assert(err, chk.IsNil) s.containerIds = append(s.containerIds, id) hostContainerId := fmt.Sprintf("%v/%v", s.daemonURI, id) cmd := exec.Command("/usr/bin/gear", "install", TestImage, hostContainerId) data, err := cmd.CombinedOutput() c.Log(string(data)) c.Assert(err, chk.IsNil) s.assertContainerState(c, id, CONTAINER_STOPPED) s.assertFilePresent(c, id.UnitPathFor(), 0664, true) paths, err := filepath.Glob(id.VersionedUnitPathFor("*")) c.Assert(err, chk.IsNil) for _, p := range paths { s.assertFilePresent(c, p, 0664, true) } s.assertFileAbsent(c, filepath.Join(id.HomePath(), "container-init.sh")) ports, err := containers.GetExistingPorts(id) c.Assert(err, chk.IsNil) c.Assert(len(ports), chk.Equals, 0) cmd = exec.Command("/usr/bin/gear", "status", hostContainerId) data, err = cmd.CombinedOutput() c.Assert(err, chk.IsNil) c.Log(string(data)) c.Assert(strings.Contains(string(data), "Loaded: loaded (/var/lib/containers/units/In/ctr-IntTest000.service; enabled)"), chk.Equals, true) s.assertContainerState(c, id, CONTAINER_STOPPED) }
func (s *IntegrationTestSuite) TestLongContainerName(c *chk.C) { id, err := containers.NewIdentifier("IntTest006xxxxxxxxxxxxxx") c.Assert(err, chk.IsNil) s.containerIds = append(s.containerIds, id) hostContainerId := fmt.Sprintf("%v/%v", s.daemonURI, id) cmd := exec.Command("/usr/bin/gear", "install", TestImage, hostContainerId, "--start", "--ports=8080:0", "--isolate") data, err := cmd.CombinedOutput() c.Log(string(data)) c.Assert(err, chk.IsNil) s.assertContainerStarts(c, id) s.assertFilePresent(c, id.UnitPathFor(), 0664, true) s.assertFilePresent(c, filepath.Join(id.RunPathFor(), "container-init.sh"), 0700, false) ports, err := containers.GetExistingPorts(id) c.Assert(err, chk.IsNil) c.Assert(len(ports), chk.Equals, 1) httpAlive := func() bool { resp, err := http.Get(fmt.Sprintf("http://0.0.0.0:%v", ports[0].External)) if err == nil { c.Assert(resp.StatusCode, chk.Equals, 200) return true } return false } if !until(TimeoutContainerStateChange, IntervalHttpCheck, httpAlive) { c.Errorf("Unable to retrieve a 200 status code from port %d", ports[0].External) c.FailNow() } }
func (j *ContainerPortsRequest) Execute(resp jobs.Response) { portPairs, err := containers.GetExistingPorts(j.Id) if err != nil { log.Printf("job_container_ports_log: Unable to find unit: %s\n", err.Error()) resp.Failure(ErrContainerNotFound) return } resp.SuccessWithData(jobs.ResponseAccepted, ContainerPortsResponse{portPairs}) }
func UnidleContainer(id containers.Identifier, hostIp string) { portPairs, err := containers.GetExistingPorts(id) if err != nil { fmt.Printf("UnidleContainer: Error retrieving ports for container: %v\n", id) return } for _, portPair := range portPairs { port := portPair.External runIptablesRules(false, true, hostIp, port.String(), id) runIptablesRules(false, true, localhost, port.String(), id) runIptablesRules(false, false, localhost, port.String(), id) runIptablesRules(true, false, localhost, port.String(), id) } }
func (idler *Idler) idleContainer(id containers.Identifier) bool { portPairs, err := containers.GetExistingPorts(id) if err != nil { fmt.Printf("idler.idleContainer: Error retrieving ports for container: %v\n", id) return false } iptablePorts, err := iptables.GetIdlerRules(id, false) if err != nil { fmt.Printf("idler.idleContainer: Error retrieving ports from iptables: %v\n", id) return false } shouldRecreateRules := false for _, portPair := range portPairs { extPort := strconv.Itoa(int(portPair.External)) shouldRecreateRules = shouldRecreateRules || !iptablePorts[extPort] } if !shouldRecreateRules { return false } //TODO: Ask geard to idle container f, err := os.Create(id.IdleUnitPathFor()) if err != nil { fmt.Printf("idler.idleContainer: Could not create idle marker for %s: %v", id.UnitNameFor(), err) return false } f.Close() if err := systemd.Connection().StopUnitJob(id.UnitNameFor(), "fail"); err != nil { fmt.Printf("idler.idleContainer: Could not stop container %s: %v", id.UnitNameFor(), err) return false } iptables.IdleContainer(id, idler.hostIp) return true }
func InitPreStart(dockerSocket string, id containers.Identifier, imageName string) error { var ( err error imgInfo *dc.Image d *docker.DockerClient ) _, socketActivationType, err := containers.GetSocketActivation(id) if err != nil { fmt.Printf("init_pre_start: Error while parsing unit file: %v\n", err) return err } if _, err = user.Lookup(id.LoginFor()); err != nil { if _, ok := err.(user.UnknownUserError); !ok { return err } if err = createUser(id); err != nil { return err } } if d, err = docker.GetConnection(dockerSocket); err != nil { return err } if imgInfo, err = d.GetImage(imageName); err != nil { return err } if err := os.MkdirAll(id.HomePath(), 0700); err != nil { return err } u, _ := user.Lookup(id.LoginFor()) volumes := make([]string, 0, 10) for volPath := range imgInfo.Config.Volumes { volumes = append(volumes, volPath) } user := imgInfo.Config.User if user == "" { user = "******" } ports, err := containers.GetExistingPorts(id) if err != nil { fmt.Printf("container init pre-start: Unable to retrieve port mapping\n") return err } containerData := containers.ContainerInitScript{ imgInfo.Config.User == "", user, u.Uid, u.Gid, strings.Join(imgInfo.Config.Cmd, " "), len(volumes) > 0, strings.Join(volumes, " "), ports, socketActivationType == "proxied", } file, _, err := utils.OpenFileExclusive(path.Join(id.RunPathFor(), "container-init.sh"), 0700) if err != nil { fmt.Printf("container init pre-start: Unable to open script file: %v\n", err) return err } defer file.Close() if erre := containers.ContainerInitTemplate.Execute(file, containerData); erre != nil { fmt.Printf("container init pre-start: Unable to output template: ", erre) return erre } if err := file.Close(); err != nil { return err } file, _, err = utils.OpenFileExclusive(path.Join(id.RunPathFor(), "container-cmd.sh"), 0705) if err != nil { fmt.Printf("container init pre-start: Unable to open cmd script file: %v\n", err) return err } defer file.Close() if erre := containers.ContainerCmdTemplate.Execute(file, containerData); erre != nil { fmt.Printf("container init pre-start: Unable to output cmd template: ", erre) return erre } if err := file.Close(); err != nil { return err } return nil }
func (j *DeleteContainerRequest) Execute(resp JobResponse) { unitName := j.Id.UnitNameFor() unitPath := j.Id.UnitPathFor() unitDefinitionsPath := j.Id.VersionedUnitsPathFor() idleFlagPath := j.Id.IdleUnitPathFor() socketUnitPath := j.Id.SocketUnitPathFor() homeDirPath := j.Id.BaseHomePath() networkLinksPath := j.Id.NetworkLinksPathFor() _, err := systemd.Connection().GetUnitProperties(unitName) switch { case systemd.IsNoSuchUnit(err): resp.Success(JobResponseOk) return case err != nil: resp.Failure(ErrDeleteContainerFailed) return } if err := systemd.Connection().StopUnitJob(unitName, "fail"); err != nil { log.Printf("delete_container: Unable to queue stop unit job: %v", err) } ports, err := containers.GetExistingPorts(j.Id) if err != nil { if !os.IsNotExist(err) { log.Printf("delete_container: Unable to read existing port definitions: %v", err) } ports = port.PortPairs{} } if err := port.ReleaseExternalPorts(ports); err != nil { log.Printf("delete_container: Unable to release ports: %v", err) } if err := os.Remove(unitPath); err != nil && !os.IsNotExist(err) { resp.Failure(ErrDeleteContainerFailed) return } if err := os.Remove(idleFlagPath); err != nil && !os.IsNotExist(err) { resp.Failure(ErrDeleteContainerFailed) return } if err := j.Id.SetUnitStartOnBoot(false); err != nil { log.Printf("delete_container: Unable to clear unit boot state: %v", err) } if err := os.Remove(socketUnitPath); err != nil && !os.IsNotExist(err) { log.Printf("delete_container: Unable to remove socket unit path: %v", err) } if err := os.Remove(networkLinksPath); err != nil && !os.IsNotExist(err) { log.Printf("delete_container: Unable to remove network links file: %v", err) } if err := os.RemoveAll(unitDefinitionsPath); err != nil { log.Printf("delete_container: Unable to remove definitions for container: %v", err) } if err := os.RemoveAll(filepath.Dir(homeDirPath)); err != nil { log.Printf("delete_container: Unable to remove home directory: %v", err) } if _, err := systemd.Connection().DisableUnitFiles([]string{unitPath, socketUnitPath}, false); err != nil { log.Printf("delete_container: Some units have not been disabled: %v", err) } if err := systemd.Connection().Reload(); err != nil { log.Printf("delete_container: Some units have not been disabled: %v", err) } resp.Success(JobResponseOk) }
func (req *InstallContainerRequest) Execute(resp jobs.Response) { id := req.Id unitName := id.UnitNameFor() unitPath := id.UnitPathFor() unitVersionPath := id.VersionedUnitPathFor(req.RequestIdentifier.String()) socketUnitName := id.SocketUnitNameFor() socketUnitPath := id.SocketUnitPathFor() var socketActivationType string if req.SocketActivation { socketActivationType = "enabled" if !req.SkipSocketProxy { socketActivationType = "proxied" } } // attempt to download the environment if it is remote env := req.Environment if env != nil { if err := env.Fetch(100 * 1024); err != nil { resp.Failure(ErrContainerCreateFailed) return } if env.Empty() { env = nil } } // open and lock the base path (to prevent simultaneous updates) state, exists, err := utils.OpenFileExclusive(unitPath, 0664) if err != nil { log.Print("install_container: Unable to lock unit file: ", err) resp.Failure(ErrContainerCreateFailed) } defer state.Close() // write a new file to disk that describes the new service unit, err := utils.CreateFileExclusive(unitVersionPath, 0664) if err != nil { log.Print("install_container: Unable to open unit file definition: ", err) resp.Failure(ErrContainerCreateFailed) return } defer unit.Close() // if this is an existing container, read the currently reserved ports existingPorts := port.PortPairs{} if exists { existingPorts, err = containers.GetExistingPorts(id) if err != nil { if _, ok := err.(*os.PathError); !ok { log.Print("install_container: Unable to read existing ports from file: ", err) resp.Failure(ErrContainerCreateFailed) return } } } // allocate and reserve ports for this container reserved, erra := port.AtomicReserveExternalPorts(unitVersionPath, req.Ports, existingPorts) if erra != nil { log.Printf("install_container: Unable to reserve external ports: %+v", erra) resp.Failure(ErrContainerCreateFailedPortsReserved) return } if len(reserved) > 0 { resp.WritePendingSuccess(PendingPortMappingName, reserved) } var portSpec string if req.Simple && len(reserved) == 0 { portSpec = "-P" } else { portSpec = dockerPortSpec(reserved) } // write the environment to disk var environmentPath string if env != nil { if errw := env.Write(false); errw != nil { resp.Failure(ErrContainerCreateFailed) return } environmentPath = env.Id.EnvironmentPathFor() } // write the network links (if any) to disk if req.NetworkLinks != nil { if errw := req.NetworkLinks.Write(id.NetworkLinksPathFor(), false); errw != nil { resp.Failure(ErrContainerCreateFailed) return } } slice := "container-small" // write the definition unit file args := csystemd.ContainerUnit{ Id: id, Image: req.Image, PortSpec: portSpec, Slice: slice + ".slice", Isolate: req.Isolate, ReqId: req.RequestIdentifier.String(), HomeDir: id.HomePath(), RunDir: id.RunPathFor(), EnvironmentPath: environmentPath, ExecutablePath: filepath.Join("/", "usr", "bin", "gear"), IncludePath: "", PortPairs: reserved, SocketUnitName: socketUnitName, SocketActivationType: socketActivationType, DockerFeatures: config.SystemDockerFeatures, } var templateName string switch { case req.SocketActivation: templateName = "SOCKETACTIVATED" case config.SystemDockerFeatures.ForegroundRun: templateName = "FOREGROUND" default: templateName = "SIMPLE" } if erre := csystemd.ContainerUnitTemplate.ExecuteTemplate(unit, templateName, args); erre != nil { log.Printf("install_container: Unable to output template: %+v", erre) resp.Failure(ErrContainerCreateFailed) defer os.Remove(unitVersionPath) return } if err := unit.Close(); err != nil { log.Printf("install_container: Unable to finish writing unit: %+v", err) resp.Failure(ErrContainerCreateFailed) defer os.Remove(unitVersionPath) return } // swap the new definition with the old one if err := utils.AtomicReplaceLink(unitVersionPath, unitPath); err != nil { log.Printf("install_container: Failed to activate new unit: %+v", err) resp.Failure(ErrContainerCreateFailed) return } state.Close() // write whether this container should be started on next boot if req.Started { if errs := csystemd.SetUnitStartOnBoot(id, true); errs != nil { log.Print("install_container: Unable to write container boot link: ", err) resp.Failure(ErrContainerCreateFailed) return } } // Generate the socket file and ignore failures paths := []string{unitPath} if req.SocketActivation { if err := writeSocketUnit(socketUnitPath, &args); err == nil { paths = []string{unitPath, socketUnitPath} } } if err := systemd.EnableAndReloadUnit(systemd.Connection(), unitName, paths...); err != nil { log.Printf("install_container: Could not enable container %s (%v): %v", unitName, paths, err) resp.Failure(ErrContainerCreateFailed) return } if req.Started { if req.SocketActivation { // Start the socket file, not the service and ignore failures if err := systemd.Connection().StartUnitJob(socketUnitName, "replace"); err != nil { log.Printf("install_container: Could not start container socket %s: %v", socketUnitName, err) resp.Failure(ErrContainerCreateFailed) return } } else { if err := systemd.Connection().StartUnitJob(unitName, "replace"); err != nil { log.Printf("install_container: Could not start container %s: %v", unitName, err) resp.Failure(ErrContainerCreateFailed) return } } } w := resp.SuccessWithWrite(jobs.ResponseAccepted, true, false) if req.Started { fmt.Fprintf(w, "Container %s is starting\n", id) } else { fmt.Fprintf(w, "Container %s is installed\n", id) } }
// FIXME: Refactor into separate responsibilities for file creation, templating, and disk access func generateAuthorizedKeys(id containers.Identifier, u *user.User, forceCreate, printToStdOut bool) error { var ( err error sshKeys []string destFile *os.File srcFile *os.File w *bufio.Writer ) var authorizedKeysPortSpec string ports, err := containers.GetExistingPorts(id) if err != nil { fmt.Errorf("container init pre-start: Unable to retrieve port mapping") return err } for _, port := range ports { authorizedKeysPortSpec += fmt.Sprintf("permitopen=\"127.0.0.1:%v\",", port.External) } sshKeys, err = filepath.Glob(path.Join(SshAccessBasePath(id), "*")) if !printToStdOut { os.MkdirAll(id.HomePath(), 0700) os.Mkdir(path.Join(id.HomePath(), ".ssh"), 0700) authKeysPath := id.AuthKeysPathFor() if _, err = os.Stat(authKeysPath); err != nil { if !os.IsNotExist(err) { return err } } else { if forceCreate { os.Remove(authKeysPath) } else { return nil } } if destFile, err = os.Create(authKeysPath); err != nil { return err } defer destFile.Close() w = bufio.NewWriter(destFile) } else { w = bufio.NewWriter(os.Stdout) } for _, keyFile := range sshKeys { s, err := os.Stat(keyFile) if err != nil { continue } if s.IsDir() { continue } srcFile, err = os.Open(keyFile) defer srcFile.Close() w.WriteString(fmt.Sprintf("command=\"/usr/bin/switchns\",%vno-agent-forwarding,no-X11-forwarding ", authorizedKeysPortSpec)) io.Copy(w, srcFile) w.WriteString("\n") } w.Flush() if !printToStdOut { uid, _ := strconv.Atoi(u.Uid) gid, _ := strconv.Atoi(u.Gid) for _, path := range []string{ id.HomePath(), filepath.Join(id.HomePath(), ".ssh"), filepath.Join(id.HomePath(), ".ssh", "authorized_keys"), } { if err := os.Chown(path, uid, gid); err != nil { return err } } if err := selinux.RestoreCon(id.BaseHomePath(), true); err != nil { return err } } return nil }
func (s *IntegrationTestSuite) TestInstallVolume(c *chk.C) { id, err := containers.NewIdentifier("TestInstallVolume") c.Assert(err, chk.IsNil) s.containerIds = append(s.containerIds, id) hostContainerId := fmt.Sprintf("%v/%v", s.daemonURI, id) mountPath, err := ioutil.TempDir("/tmp", "bind-rw") c.Assert(err, chk.IsNil) roMountPath, err := ioutil.TempDir("/tmp", "bind-ro") c.Assert(err, chk.IsNil) roTestFilePath := path.Join(roMountPath, "ro-test") ioutil.WriteFile(roTestFilePath, []byte{}, 0664) cmd := exec.Command("/usr/bin/gear", "install", TestImage, hostContainerId, fmt.Sprintf("--volumes=/test-volume,%s:/test-bind-ro:ro,%s:/test-bind-rw", roMountPath, mountPath), "--ports=8080:0", "--start") data, err := cmd.CombinedOutput() c.Log(string(data)) c.Assert(err, chk.IsNil) s.assertContainerStarts(c, id) oldPid := s.getContainerPid(id) ports, err := containers.GetExistingPorts(id) c.Assert(err, chk.IsNil) c.Assert(len(ports), chk.Equals, 1) httpAlive := func() bool { resp, err := http.Get(fmt.Sprintf("http://0.0.0.0:%v", ports[0].External)) if err == nil { c.Assert(resp.StatusCode, chk.Equals, 200) return true } return false } if !until(TimeoutContainerStateChange, IntervalHttpCheck, httpAlive) { c.Errorf("Unable to retrieve a 200 status code from port %d", ports[0].External) c.FailNow() } exitCode, err := namespace.RunCommandInContainer(s.dockerClient, "TestInstallVolume", []string{"/bin/busybox", "ls", "/test-bind-ro/ro-test"}, []string{}) c.Assert(err, chk.IsNil) c.Assert(exitCode, chk.Equals, 0) exitCode, err = namespace.RunCommandInContainer(s.dockerClient, "TestInstallVolume", []string{"/bin/busybox", "touch", "/test-bind-ro/rw-test"}, []string{}) c.Assert(err, chk.IsNil) c.Assert(exitCode, chk.Not(chk.Equals), 0) exitCode, err = namespace.RunCommandInContainer(s.dockerClient, "TestInstallVolume", []string{"/bin/busybox", "touch", "/test-bind-rw/rw-test"}, []string{}) c.Assert(err, chk.IsNil) c.Assert(exitCode, chk.Equals, 0) exitCode, err = namespace.RunCommandInContainer(s.dockerClient, "TestInstallVolume", []string{"/bin/busybox", "touch", "/test-volume/rw-test"}, []string{}) c.Assert(err, chk.IsNil) c.Assert(exitCode, chk.Equals, 0) exitCode, err = namespace.RunCommandInContainer(s.dockerClient, "TestInstallVolume", []string{"/bin/busybox", "touch", "/tmp/transient-file"}, []string{}) c.Assert(err, chk.IsNil) c.Assert(exitCode, chk.Equals, 0) cmd = exec.Command("/usr/bin/gear", "restart", hostContainerId) data, err = cmd.CombinedOutput() c.Log(string(data)) c.Assert(err, chk.IsNil) s.assertContainerRestarts(c, id) newPid := s.getContainerPid(id) c.Assert(oldPid, chk.Not(chk.Equals), newPid) exitCode, err = namespace.RunCommandInContainer(s.dockerClient, "TestInstallVolume", []string{"/bin/busybox", "ls", "/test-bind-rw/rw-test"}, []string{}) c.Assert(err, chk.IsNil) c.Assert(exitCode, chk.Equals, 0) exitCode, err = namespace.RunCommandInContainer(s.dockerClient, "TestInstallVolume", []string{"/bin/busybox", "ls", "/test-volume/rw-test"}, []string{}) c.Assert(err, chk.IsNil) c.Assert(exitCode, chk.Equals, 0) exitCode, err = namespace.RunCommandInContainer(s.dockerClient, "TestInstallVolume", []string{"/bin/busybox", "ls", "/tmp/transient-file"}, []string{}) c.Assert(err, chk.IsNil) c.Assert(exitCode, chk.Not(chk.Equals), 0) }