Don't block the HTTP request while waiting on the installation

This commit is contained in:
Dane Everitt 2019-12-28 15:12:12 -08:00
parent 6ef2773c01
commit 5350a2d5a5
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
2 changed files with 35 additions and 32 deletions

View File

@ -431,7 +431,7 @@ func (rt *Router) routeServerInstall(w http.ResponseWriter, r *http.Request, ps
return return
} }
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusAccepted)
} }
func (rt *Router) routeServerUpdate(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { func (rt *Router) routeServerUpdate(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

View File

@ -33,7 +33,28 @@ func (s *Server) Install() error {
return errors.WithStack(err) return errors.WithStack(err)
} }
return p.Execute() go func(proc *InstallationProcess) {
installPath, err := proc.BeforeExecute()
if err != nil {
zap.S().Errorw(
"failed to complete BeforeExecute step of installation process",
zap.String("server", proc.Server.Uuid),
zap.Error(errors.WithStack(err)),
)
return
}
if err := proc.Execute(installPath); err != nil {
zap.S().Errorw(
"failed to complete Execute step of installation process",
zap.String("server", proc.Server.Uuid),
zap.Error(errors.WithStack(err)),
)
}
}(p)
return nil
} }
type InstallationProcess struct { type InstallationProcess struct {
@ -80,7 +101,7 @@ func (ip *InstallationProcess) writeScriptToDisk() (string, error) {
scanner := bufio.NewScanner(bytes.NewReader([]byte(ip.Script.Script))) scanner := bufio.NewScanner(bytes.NewReader([]byte(ip.Script.Script)))
for scanner.Scan() { for scanner.Scan() {
w.WriteString(scanner.Text()+"\n") w.WriteString(scanner.Text() + "\n")
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
@ -111,7 +132,7 @@ func (ip *InstallationProcess) pullInstallationImage() error {
// Runs before the container is executed. This pulls down the required docker container image // Runs before the container is executed. This pulls down the required docker container image
// as well as writes the installation script to the disk. This process is executed in an async // as well as writes the installation script to the disk. This process is executed in an async
// manner, if either one fails the error is returned. // manner, if either one fails the error is returned.
func (ip *InstallationProcess) beforeExecute() (string, error) { func (ip *InstallationProcess) BeforeExecute() (string, error) {
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(3) wg.Add(3)
@ -163,18 +184,13 @@ func (ip *InstallationProcess) beforeExecute() (string, error) {
} }
// Executes the installation process inside a specially created docker container. // Executes the installation process inside a specially created docker container.
func (ip *InstallationProcess) Execute() error { func (ip *InstallationProcess) Execute(installPath string) error {
installScriptPath, err := ip.beforeExecute()
if err != nil {
return errors.WithStack(err)
}
ctx := context.Background() ctx := context.Background()
zap.S().Debugw( zap.S().Debugw(
"creating server installer container", "creating server installer container",
zap.String("server", ip.Server.Uuid), zap.String("server", ip.Server.Uuid),
zap.String("script_path", installScriptPath+"/install.sh"), zap.String("script_path", installPath+"/install.sh"),
) )
conf := &container.Config{ conf := &container.Config{
@ -203,7 +219,7 @@ func (ip *InstallationProcess) Execute() error {
}, },
{ {
Target: "/mnt/install", Target: "/mnt/install",
Source: installScriptPath, Source: installPath,
Type: mount.TypeBind, Type: mount.TypeBind,
ReadOnly: false, ReadOnly: false,
}, },
@ -235,27 +251,14 @@ func (ip *InstallationProcess) Execute() error {
return err return err
} }
stream, err := ip.client.ContainerAttach(ctx, r.ID, types.ContainerAttachOptions{ sChann, eChann := ip.client.ContainerWait(ctx, r.ID, container.WaitConditionNotRunning)
Stdout: true, select {
Stderr: true, case err := <-eChann:
Stream: true,
})
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
case <-sChann:
wg := sync.WaitGroup{} }
wg.Add(1)
go func() {
defer stream.Close()
defer wg.Done()
io.Copy(os.Stdout, stream.Reader)
}()
wg.Wait()
zap.S().Infow("completed installation process", zap.String("server", ip.Server.Uuid)) zap.S().Infow("completed installation process", zap.String("server", ip.Server.Uuid))