diff --git a/environment/docker/container.go b/environment/docker/container.go index e468e89..1e857f1 100644 --- a/environment/docker/container.go +++ b/environment/docker/container.go @@ -3,6 +3,7 @@ package docker import ( "bufio" "context" + "encoding/json" "fmt" "github.com/apex/log" "github.com/docker/docker/api/types" @@ -19,6 +20,11 @@ import ( "time" ) +type imagePullStatus struct { + Status string `json:"status"` + Progress string `json:"progress"` +} + // Attaches to the docker container itself and ensures that we can pipe data in and out // of the process stream. This should not be used for reading console data as you *will* // miss important output at the beginning because of the time delay with attaching to the @@ -148,7 +154,7 @@ func (e *Environment) Create() error { // Convert 127.0.0.1 to the pterodactyl0 network interface if the environment is Docker // so that the server operates as expected. if v == "SERVER_IP=127.0.0.1" { - evs[i] = "SERVER_IP="+config.Get().Docker.Network.Interface + evs[i] = "SERVER_IP=" + config.Get().Docker.Network.Interface } } @@ -307,6 +313,9 @@ func (e *Environment) followOutput() error { // // TODO: local images func (e *Environment) ensureImageExists(image string) error { + e.Events().Publish(environment.DockerImagePullStarted, "") + defer e.Events().Publish(environment.DockerImagePullCompleted, "") + // Give it up to 15 minutes to pull the image. I think this should cover 99.8% of cases where an // image pull might fail. I can't imagine it will ever take more than 15 minutes to fully pull // an image. Let me know when I am inevitably wrong here... @@ -374,12 +383,18 @@ func (e *Environment) ensureImageExists(image string) error { // is done being pulled, which is what we need. scanner := bufio.NewScanner(out) for scanner.Scan() { - continue + s := imagePullStatus{} + fmt.Println(scanner.Text()) + if err := json.Unmarshal(scanner.Bytes(), &s); err == nil { + e.Events().Publish(environment.DockerImagePullStatus, s.Status+" "+s.Progress) + } } if err := scanner.Err(); err != nil { return err } + log.WithField("image", image).Debug("completed docker image pull") + return nil } diff --git a/environment/environment.go b/environment/environment.go index 55834c8..e8352ab 100644 --- a/environment/environment.go +++ b/environment/environment.go @@ -6,9 +6,12 @@ import ( ) const ( - ConsoleOutputEvent = "console output" - StateChangeEvent = "state change" - ResourceEvent = "resources" + ConsoleOutputEvent = "console output" + StateChangeEvent = "state change" + ResourceEvent = "resources" + DockerImagePullStarted = "docker image pull started" + DockerImagePullStatus = "docker image pull status" + DockerImagePullCompleted = "docker image pull completed" ) const ( diff --git a/server/listeners.go b/server/listeners.go index 02ef352..bcef8fc 100644 --- a/server/listeners.go +++ b/server/listeners.go @@ -11,6 +11,12 @@ import ( "strconv" ) +var dockerEvents = []string{ + environment.DockerImagePullStatus, + environment.DockerImagePullStarted, + environment.DockerImagePullCompleted, +} + // Adds all of the internal event listeners we want to use for a server. These listeners can only be // removed by deleting the server as they should last for the duration of the process' lifetime. func (s *Server) StartEventListeners() { @@ -45,10 +51,23 @@ func (s *Server) StartEventListeners() { s.emitProcUsage() } + docker := func(e events.Event) { + if e.Topic == environment.DockerImagePullStatus { + s.Events().Publish(InstallOutputEvent, e.Data) + } else if e.Topic == environment.DockerImagePullStarted { + s.PublishConsoleOutputFromDaemon("Pulling Docker container image, this could take a few minutes to complete...") + } else { + s.PublishConsoleOutputFromDaemon("Finished pulling Docker container image") + } + } + s.Log().Info("registering event listeners: console, state, resources...") s.Environment.Events().On(environment.ConsoleOutputEvent, &console) s.Environment.Events().On(environment.StateChangeEvent, &state) s.Environment.Events().On(environment.ResourceEvent, &stats) + for _, evt := range dockerEvents { + s.Environment.Events().On(evt, &docker) + } } var stripAnsiRegex = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")