Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions server/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (s *Server) SetRestoring(state bool) {
}

func (s *Server) IsInProtectedState() bool {
return s.IsInstalling() || s.IsTransferring() || s.IsRestoring()
return s.IsInstalling() || s.IsTransferring() || s.IsRestoring()
}

// RemoveContainer removes the installation container for the server.
Expand Down Expand Up @@ -214,10 +214,10 @@ func (ip *InstallationProcess) Run() error {
return err
}

cID, err := ip.Execute()
if err != nil {
cID, executeErr := ip.Execute()
if cID == "" {
_ = ip.RemoveContainer()
return err
return executeErr
}

// If this step fails, log a warning but don't exit out of the process. This is completely
Expand All @@ -226,7 +226,7 @@ func (ip *InstallationProcess) Run() error {
ip.Server.Log().WithField("error", err).Warn("failed to complete after-execute step of installation process")
}

return nil
return executeErr
}

// Returns the location of the temporary data for the installation process.
Expand Down Expand Up @@ -531,18 +531,34 @@ func (ip *InstallationProcess) Execute() (string, error) {
sChan, eChan := ip.client.ContainerWait(ctx, r.ID, container.WaitConditionNotRunning)
select {
case err := <-eChan:
// Once the container has stopped running we can mark the install process as being completed.
if err == nil {
ip.Server.Events().Publish(DaemonMessageEvent, "Installation process completed.")
} else {
return "", err
return r.ID, err
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
case response := <-sChan:
if err := installationWaitError(response); err != nil {
ip.Server.Events().Publish(DaemonMessageEvent, "Installation process failed: "+err.Error())
return r.ID, err
}
case <-sChan:
}

ip.Server.Events().Publish(DaemonMessageEvent, "Installation process completed.")
return r.ID, nil
}

func installationWaitError(response container.WaitResponse) error {
if response.Error != nil {
message := strings.TrimSpace(response.Error.Message)
if message == "" {
message = "unknown container wait error"
}

return errors.Errorf("install: installation container wait failed: %s", message)
}
if response.StatusCode != 0 {
return errors.Errorf("install: installation script exited with code %d", response.StatusCode)
}

return nil
}

// StreamOutput streams the output of the installation process to a log file in
// the server configuration directory, as well as to a websocket listener so
// that the process can be viewed in the panel by administrators.
Expand Down
63 changes: 63 additions & 0 deletions server/install_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package server

import (
"testing"

"github.com/docker/docker/api/types/container"
)

func TestInstallationWaitError(t *testing.T) {
tests := []struct {
name string
response container.WaitResponse
wantError string
}{
{
name: "successful installation",
response: container.WaitResponse{StatusCode: 0},
},
{
name: "installation script failure",
response: container.WaitResponse{StatusCode: 1},
wantError: "install: installation script exited with code 1",
},
{
name: "command not found",
response: container.WaitResponse{StatusCode: 127},
wantError: "install: installation script exited with code 127",
},
{
name: "container wait failure",
response: container.WaitResponse{
Error: &container.WaitExitError{Message: "daemon disconnected"},
},
wantError: "install: installation container wait failed: daemon disconnected",
},
{
name: "container wait failure without message",
response: container.WaitResponse{
Error: &container.WaitExitError{},
},
wantError: "install: installation container wait failed: unknown container wait error",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := installationWaitError(tt.response)
if tt.wantError == "" {
if err != nil {
t.Fatalf("installationWaitError() returned unexpected error: %v", err)
}
return
}

if err == nil {
t.Fatalf("installationWaitError() returned nil, expected %q", tt.wantError)
}
if err.Error() != tt.wantError {
t.Fatalf("installationWaitError() error = %q, expected %q", err.Error(), tt.wantError)
}
})
}
}