Merge branch '21126-trash-when-ro'
[arvados.git] / cmd / arvados-package / install.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "context"
9         "fmt"
10         "io"
11         "io/ioutil"
12         "os"
13         "os/exec"
14         "path/filepath"
15         "strings"
16
17         "git.arvados.org/arvados.git/lib/crunchrun"
18         "github.com/docker/docker/api/types"
19         "github.com/docker/docker/client"
20 )
21
22 func testinstall(ctx context.Context, opts opts, stdin io.Reader, stdout, stderr io.Writer) error {
23         depsImageName := "arvados-package-deps-" + opts.TargetOS
24         depsCtrName := strings.Replace(depsImageName, ":", "-", -1)
25         absPackageDir, err := filepath.Abs(opts.PackageDir)
26         if err != nil {
27                 return fmt.Errorf("error resolving PackageDir %q: %w", opts.PackageDir, err)
28         }
29
30         _, prog := filepath.Split(os.Args[0])
31         tmpdir, err := ioutil.TempDir("", prog+".")
32         if err != nil {
33                 return fmt.Errorf("TempDir: %w", err)
34         }
35         defer os.RemoveAll(tmpdir)
36
37         if exists, err := dockerImageExists(ctx, depsImageName); err != nil {
38                 return err
39         } else if !exists || opts.RebuildImage {
40                 err = dockerRm(ctx, depsCtrName)
41                 if err != nil {
42                         return err
43                 }
44                 defer dockerRm(ctx, depsCtrName)
45                 cmd := exec.CommandContext(ctx, "docker", "run",
46                         "--name", depsCtrName,
47                         "--tmpfs", "/tmp:exec,mode=01777",
48                         "-v", absPackageDir+":/pkg:ro",
49                         "--env", "DEBIAN_FRONTEND=noninteractive",
50                         opts.TargetOS,
51                         "bash", "-c", `
52 set -e -o pipefail
53 apt-get --allow-releaseinfo-change update
54 apt-get install -y --no-install-recommends dpkg-dev eatmydata
55
56 mkdir /tmp/pkg
57 ln -s /pkg/*.deb /tmp/pkg/
58 (cd /tmp/pkg; dpkg-scanpackages --multiversion . | gzip > Packages.gz)
59 echo >/etc/apt/sources.list.d/arvados-local.list "deb [trusted=yes] file:///tmp/pkg ./"
60 apt-get --allow-releaseinfo-change update
61
62 eatmydata apt-get install -y --no-install-recommends arvados-server-easy postgresql
63 eatmydata apt-get remove -y dpkg-dev
64 SUDO_FORCE_REMOVE=yes apt-get autoremove -y
65 eatmydata apt-get remove -y arvados-server-easy
66 rm /etc/apt/sources.list.d/arvados-local.list
67 `)
68                 cmd.Stdout = stdout
69                 cmd.Stderr = stderr
70                 err = cmd.Run()
71                 if err != nil {
72                         return fmt.Errorf("%v: %w", cmd.Args, err)
73                 }
74
75                 cmd = exec.CommandContext(ctx, "docker", "commit", depsCtrName, depsImageName)
76                 cmd.Stdout = stdout
77                 cmd.Stderr = stderr
78                 err = cmd.Run()
79                 if err != nil {
80                         return fmt.Errorf("%v: %w", cmd.Args, err)
81                 }
82         }
83
84         versionsuffix := ""
85         if opts.PackageVersion != "" {
86                 versionsuffix = "=" + opts.PackageVersion
87         }
88         cmd := exec.CommandContext(ctx, "docker", "run", "--rm",
89                 "--tmpfs=/tmp:exec,mode=01777",
90                 "--volume="+absPackageDir+":/pkg:ro",
91                 "--env=DEBIAN_FRONTEND=noninteractive")
92         if opts.Live != "" {
93                 cmd.Args = append(cmd.Args,
94                         "--env=domain="+opts.Live,
95                         "--env=initargs=-tls=/var/lib/acme/live/"+opts.Live,
96                         "--env=bootargs=",
97                         "--publish=:443:443",
98                         "--publish=:4440-4460:4440-4460",
99                         "--publish=:9000-9020:9000-9020",
100                         "--add-host="+opts.Live+":0.0.0.0",
101                         "--volume=/var/lib/acme:/var/lib/acme:ro")
102         } else {
103                 cmd.Args = append(cmd.Args,
104                         "--env=domain=localhost",
105                         "--env=initargs=-tls=insecure",
106                         "--env=bootargs=-shutdown")
107         }
108         cmd.Args = append(cmd.Args,
109                 depsImageName,
110                 "bash", "-c", `
111 set -e -o pipefail
112 PATH="/var/lib/arvados/bin:$PATH"
113 apt-get --allow-releaseinfo-change update
114 apt-get install -y --no-install-recommends dpkg-dev
115 mkdir /tmp/pkg
116 ln -s /pkg/*.deb /tmp/pkg/
117 (cd /tmp/pkg; dpkg-scanpackages --multiversion . | gzip > Packages.gz)
118 apt-get remove -y dpkg-dev
119 echo
120
121 echo >/etc/apt/sources.list.d/arvados-local.list "deb [trusted=yes] file:///tmp/pkg ./"
122 apt-get --allow-releaseinfo-change update
123 eatmydata apt-get install --reinstall -y --no-install-recommends arvados-server-easy`+versionsuffix+`
124 SUDO_FORCE_REMOVE=yes apt-get autoremove -y
125
126 /etc/init.d/postgresql start
127 arvados-server init -cluster-id x1234 -domain=$domain -login=test -start=false $initargs
128 exec arvados-server boot -listen-host=0.0.0.0 $bootargs
129 `)
130         cmd.Stdout = stdout
131         cmd.Stderr = stderr
132         err = cmd.Run()
133         if err != nil {
134                 return fmt.Errorf("%v: %w", cmd.Args, err)
135         }
136         return nil
137 }
138
139 func dockerImageExists(ctx context.Context, name string) (bool, error) {
140         cli, err := client.NewClient(client.DefaultDockerHost, crunchrun.DockerAPIVersion, nil, nil)
141         if err != nil {
142                 return false, err
143         }
144         imgs, err := cli.ImageList(ctx, types.ImageListOptions{All: true})
145         if err != nil {
146                 return false, err
147         }
148         for _, img := range imgs {
149                 for _, tag := range img.RepoTags {
150                         if tag == name {
151                                 return true, nil
152                         }
153                 }
154         }
155         return false, nil
156 }