1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
19 "git.arvados.org/arvados.git/lib/crunchrun"
20 "git.arvados.org/arvados.git/sdk/go/ctxlog"
21 "github.com/docker/docker/api/types"
22 "github.com/docker/docker/client"
25 func build(ctx context.Context, opts opts, stdin io.Reader, stdout, stderr io.Writer) error {
26 if opts.PackageVersion == "" {
28 cmd := exec.CommandContext(ctx, "bash", "./build/version-at-commit.sh", "HEAD")
31 cmd.Dir = opts.SourceDir
34 return fmt.Errorf("%v: %w", cmd.Args, err)
36 opts.PackageVersion = strings.TrimSpace(buf.String())
37 ctxlog.FromContext(ctx).Infof("version not specified; using %s", opts.PackageVersion)
40 if opts.PackageChown == "" {
41 whoami, err := user.Current()
43 return fmt.Errorf("user.Current: %w", err)
45 opts.PackageChown = whoami.Uid + ":" + whoami.Gid
48 // Build in a tempdir, then move to the desired destination
49 // dir. Otherwise, errors might cause us to leave a mess:
50 // truncated files, files owned by root, etc.
51 _, prog := filepath.Split(os.Args[0])
52 tmpdir, err := ioutil.TempDir(opts.PackageDir, prog+".")
56 defer os.RemoveAll(tmpdir)
57 if abs, err := filepath.Abs(tmpdir); err != nil {
58 return fmt.Errorf("error getting absolute path of tmpdir %s: %w", tmpdir, err)
63 selfbin, err := os.Readlink("/proc/self/exe")
65 return fmt.Errorf("readlink /proc/self/exe: %w", err)
67 buildImageName := "arvados-package-build-" + opts.TargetOS
68 packageFilename := "arvados-server-easy_" + opts.PackageVersion + "_amd64.deb"
70 if ok, err := dockerImageExists(ctx, buildImageName); err != nil {
72 } else if !ok || opts.RebuildImage {
73 buildCtrName := strings.Replace(buildImageName, ":", "-", -1)
74 err = dockerRm(ctx, buildCtrName)
79 defer dockerRm(ctx, buildCtrName)
80 cmd := exec.CommandContext(ctx, "docker", "run",
81 "--name", buildCtrName,
82 "--tmpfs", "/tmp:exec,mode=01777",
83 "-v", selfbin+":/arvados-package:ro",
84 "-v", opts.SourceDir+":/arvados:ro",
86 "/arvados-package", "_install",
89 "-source", "/arvados",
90 "-package-version", opts.PackageVersion,
96 return fmt.Errorf("%v: %w", cmd.Args, err)
99 cmd = exec.CommandContext(ctx, "docker", "commit", buildCtrName, buildImageName)
104 return fmt.Errorf("docker commit: %w", err)
107 ctxlog.FromContext(ctx).Infof("created docker image %s", buildImageName)
110 cmd := exec.CommandContext(ctx, "docker", "run",
112 "--tmpfs", "/tmp:exec,mode=01777",
113 "-v", tmpdir+":/pkg",
114 "-v", selfbin+":/arvados-package:ro",
115 "-v", opts.SourceDir+":/arvados:ro",
117 "eatmydata", "/arvados-package", "_fpm",
118 "-source", "/arvados",
119 "-package-version", opts.PackageVersion,
120 "-package-dir", "/pkg",
121 "-package-chown", opts.PackageChown,
122 "-package-maintainer", opts.Maintainer,
123 "-package-vendor", opts.Vendor,
129 return fmt.Errorf("%v: %w", cmd.Args, err)
132 err = os.Rename(tmpdir+"/"+packageFilename, opts.PackageDir+"/"+packageFilename)
136 cmd = exec.CommandContext(ctx, "ls", "-l", opts.PackageDir+"/"+packageFilename)
147 func dockerRm(ctx context.Context, name string) error {
148 cli, err := client.NewClient(client.DefaultDockerHost, crunchrun.DockerAPIVersion, nil, nil)
152 ctrs, err := cli.ContainerList(ctx, types.ContainerListOptions{All: true, Limit: -1})
156 for _, ctr := range ctrs {
157 for _, ctrname := range ctr.Names {
158 if ctrname == "/"+name {
159 err = cli.ContainerRemove(ctx, ctr.ID, types.ContainerRemoveOptions{})
161 return fmt.Errorf("error removing container %s: %w", ctr.ID, err)