func (j *StartedContainerStateRequest) Execute(resp jobs.Response) { unitName := j.Id.UnitNameFor() unitPath := j.Id.UnitPathFor() inState, tooSoon := inStateOrTooSoon(j.Id, unitName, true, false, rateLimitChanges) if inState { w := resp.SuccessWithWrite(jobs.ResponseAccepted, true, false) fmt.Fprintf(w, "Container %s starting\n", j.Id) return } if tooSoon { resp.Failure(ErrStartRequestThrottled) return } if errs := csystemd.SetUnitStartOnBoot(j.Id, true); errs != nil { log.Print("alter_container_state: Unable to persist whether the unit is started on boot: ", errs) resp.Failure(ErrContainerStartFailed) return } if err := systemd.EnableAndReloadUnit(systemd.Connection(), unitName, unitPath); err != nil { if systemd.IsNoSuchUnit(err) || systemd.IsFileNotFound(err) { resp.Failure(ErrContainerNotFound) return } log.Printf("alter_container_state: Could not enable container %s: %v", unitName, err) resp.Failure(ErrContainerStartFailed) return } if err := systemd.Connection().StartUnitJob(unitName, "replace"); err != nil { log.Printf("alter_container_state: Could not start container %s: %v", unitName, err) resp.Failure(ErrContainerStartFailed) return } w := resp.SuccessWithWrite(jobs.ResponseAccepted, true, false) fmt.Fprintf(w, "Container %s starting\n", j.Id) }
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) } }
func (j *StoppedContainerStateRequest) Execute(resp jobs.Response) { unitName := j.Id.UnitNameFor() inState, tooSoon := inStateOrTooSoon(j.Id, unitName, false, false, rateLimitChanges) if inState { w := resp.SuccessWithWrite(jobs.ResponseAccepted, true, false) fmt.Fprintf(w, "Container %s is stopped\n", j.Id) return } if tooSoon { resp.Failure(ErrStopRequestThrottled) return } if errs := csystemd.SetUnitStartOnBoot(j.Id, false); errs != nil { log.Print("alter_container_state: Unable to persist whether the unit is started on boot: ", errs) resp.Failure(ErrContainerStopFailed) return } w := resp.SuccessWithWrite(jobs.ResponseAccepted, true, false) done := make(chan time.Time) ioerr := make(chan error) go func() { ioerr <- systemd.WriteLogsTo(w, unitName, 0, done) }() joberr := make(chan error) go func() { status, err := systemd.Connection().StopUnit(unitName, "replace") if err == nil && status != "done" { err = errors.New(fmt.Sprintf("Job status 'done' != %s", status)) } joberr <- err }() var err error select { case err = <-ioerr: log.Printf("alter_container_state: Client hung up") close(ioerr) case err = <-joberr: log.Printf("alter_container_state: Stop job done") case <-time.After(15 * time.Second): log.Printf("alter_container_state: Timeout waiting for stop completion") } close(done) select { case <-ioerr: } switch { case systemd.IsNoSuchUnit(err): if _, err := os.Stat(j.Id.UnitPathFor()); err == nil { fmt.Fprintf(w, "Container %s is stopped\n", j.Id) } else { fmt.Fprintf(w, "No such container %s\n", j.Id) } case err != nil: fmt.Fprintf(w, "Could not stop container: %s\n", err.Error()) default: fmt.Fprintf(w, "Container %s is stopped\n", j.Id) } }
func (j *deleteContainer) Execute(resp jobs.Response) { unitName := j.Id.UnitNameFor() unitPath := j.Id.UnitPathFor() unitDefinitionsPath := j.Id.VersionedUnitsPathFor() idleFlagPath := j.Id.IdleUnitPathFor() socketUnitPath := j.Id.SocketUnitPathFor() homeDirPath := j.Id.BaseHomePath() runDirPath := j.Id.RunPathFor() networkLinksPath := j.Id.NetworkLinksPathFor() _, err := systemd.Connection().GetUnitProperties(unitName) switch { case systemd.IsNoSuchUnit(err): resp.Success(jobs.ResponseOk) 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 := portReserver.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 := csystemd.SetUnitStartOnBoot(j.Id, 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(runDirPath)); err != nil { log.Printf("delete_container: Unable to remove run directory: %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(jobs.ResponseOk) }