15954: Fix deadlock.
[arvados.git] / lib / boot / passenger.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package boot
6
7 import (
8         "bytes"
9         "context"
10         "fmt"
11         "os"
12         "path/filepath"
13         "strings"
14         "sync"
15
16         "git.arvados.org/arvados.git/sdk/go/arvados"
17 )
18
19 // Don't trust "passenger-config" (or "bundle install") to handle
20 // concurrent installs.
21 var passengerInstallMutex sync.Mutex
22
23 type installPassenger struct {
24         src     string
25         depends []bootTask
26 }
27
28 func (runner installPassenger) String() string {
29         return "installPassenger:" + runner.src
30 }
31
32 func (runner installPassenger) Run(ctx context.Context, fail func(error), boot *Booter) error {
33         err := boot.wait(ctx, runner.depends...)
34         if err != nil {
35                 return err
36         }
37
38         passengerInstallMutex.Lock()
39         defer passengerInstallMutex.Unlock()
40
41         var buf bytes.Buffer
42         err = boot.RunProgram(ctx, runner.src, &buf, nil, "gem", "list", "--details", "bundler")
43         if err != nil {
44                 return err
45         }
46         for _, version := range []string{"1.11.0", "1.17.3", "2.0.2"} {
47                 if !strings.Contains(buf.String(), "("+version+")") {
48                         err = boot.RunProgram(ctx, runner.src, nil, nil, "gem", "install", "--user", "bundler:1.11", "bundler:1.17.3", "bundler:2.0.2")
49                         if err != nil {
50                                 return err
51                         }
52                         break
53                 }
54         }
55         err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "install", "--jobs", "4", "--path", filepath.Join(os.Getenv("HOME"), ".gem"))
56         if err != nil {
57                 return err
58         }
59         err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger-config", "build-native-support")
60         if err != nil {
61                 return err
62         }
63         err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger-config", "install-standalone-runtime")
64         if err != nil {
65                 return err
66         }
67         err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger-config", "validate-install")
68         if err != nil {
69                 return err
70         }
71         return nil
72 }
73
74 type runPassenger struct {
75         src     string
76         svc     arvados.Service
77         depends []bootTask
78 }
79
80 func (runner runPassenger) String() string {
81         return "runPassenger:" + runner.src
82 }
83
84 func (runner runPassenger) Run(ctx context.Context, fail func(error), boot *Booter) error {
85         err := boot.wait(ctx, runner.depends...)
86         if err != nil {
87                 return err
88         }
89         port, err := internalPort(runner.svc)
90         if err != nil {
91                 return fmt.Errorf("bug: no InternalURLs for component %q: %v", runner, runner.svc.InternalURLs)
92         }
93         go func() {
94                 err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec",
95                         "passenger", "start",
96                         "-p", port,
97                         "--log-file", "/dev/null",
98                         "--pid-file", filepath.Join(boot.tempdir, "passenger."+strings.Replace(runner.src, "/", "_", -1)+".pid"))
99                 fail(err)
100         }()
101         return nil
102 }