15370: Re-enable docker tests.
[arvados.git] / cmd / arvados-package / cmd.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         "flag"
10         "fmt"
11         "io"
12         "os"
13         "path/filepath"
14         "strings"
15
16         "git.arvados.org/arvados.git/lib/cmd"
17         "git.arvados.org/arvados.git/lib/install"
18         "git.arvados.org/arvados.git/sdk/go/ctxlog"
19 )
20
21 var (
22         handler = cmd.Multi(map[string]cmd.Handler{
23                 "version":   cmd.Version,
24                 "-version":  cmd.Version,
25                 "--version": cmd.Version,
26
27                 "build":       cmdFunc(build),
28                 "testinstall": cmdFunc(testinstall),
29                 "_fpm":        cmdFunc(fpm),    // internal use
30                 "_install":    install.Command, // internal use
31         })
32 )
33
34 func main() {
35         if len(os.Args) < 2 || strings.HasPrefix(os.Args[1], "-") {
36                 parseFlags(os.Args[0], []string{"-help"}, os.Stderr)
37                 os.Exit(2)
38         }
39         os.Exit(handler.RunCommand(os.Args[0], os.Args[1:], os.Stdin, os.Stdout, os.Stderr))
40 }
41
42 type cmdFunc func(ctx context.Context, opts opts, stdin io.Reader, stdout, stderr io.Writer) error
43
44 func (cf cmdFunc) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
45         logger := ctxlog.New(stderr, "text", "info")
46         ctx := ctxlog.Context(context.Background(), logger)
47         opts, ok, code := parseFlags(prog, args, stderr)
48         if !ok {
49                 return code
50         }
51         err := cf(ctx, opts, stdin, stdout, stderr)
52         if err != nil {
53                 logger.WithError(err).Error("failed")
54                 return 1
55         }
56         return 0
57 }
58
59 type opts struct {
60         PackageVersion string
61         PackageDir     string
62         PackageChown   string
63         RebuildImage   bool
64         SourceDir      string
65         TargetOS       string
66         Maintainer     string
67         Vendor         string
68         Live           string
69 }
70
71 func parseFlags(prog string, args []string, stderr io.Writer) (_ opts, ok bool, exitCode int) {
72         opts := opts{
73                 SourceDir:  ".",
74                 TargetOS:   "debian:10",
75                 Maintainer: "Arvados Package Maintainers <packaging@arvados.org>",
76                 Vendor:     "The Arvados Project",
77         }
78         flags := flag.NewFlagSet("", flag.ContinueOnError)
79         flags.StringVar(&opts.PackageVersion, "package-version", opts.PackageVersion, "package version to build/test, like \"1.2.3\"")
80         flags.StringVar(&opts.SourceDir, "source", opts.SourceDir, "arvados source tree `directory`")
81         flags.StringVar(&opts.PackageDir, "package-dir", opts.PackageDir, "destination `directory` for new package (default is cwd)")
82         flags.StringVar(&opts.PackageChown, "package-chown", opts.PackageChown, "desired `uid:gid` for new package (default is current user:group)")
83         flags.StringVar(&opts.TargetOS, "target-os", opts.TargetOS, "target operating system vendor:version")
84         flags.StringVar(&opts.Maintainer, "package-maintainer", opts.Maintainer, "maintainer to be listed in package metadata")
85         flags.StringVar(&opts.Vendor, "package-vendor", opts.Vendor, "vendor to be listed in package metadata")
86         flags.StringVar(&opts.Live, "live", opts.Live, "(for testinstall) advertise external URLs like https://`example.com`:44xx, use the host's /var/lib/acme/live certificates, listen on the host's external interfaces, and wait for ^C before shutting down")
87         flags.BoolVar(&opts.RebuildImage, "rebuild-image", opts.RebuildImage, "rebuild docker image(s) instead of using existing")
88         flags.Usage = func() {
89                 fmt.Fprint(flags.Output(), `Usage: arvados-package <subcommand> [options]
90
91 Subcommands:
92         build
93                 use a docker container to build a package from a checked
94                 out version of the arvados source tree
95         testinstall
96                 use a docker container to install a package and confirm
97                 the resulting installation is functional; optionally,
98                 expose the test cluster's services using the host's
99                 interfaces and ACME certificates, and leave it up to
100                 facilitate interactive testing (see -live option
101                 below)
102         version
103                 show program version
104
105 Internally used subcommands:
106         _fpm
107                 build a package
108         _install
109                 equivalent to "arvados-server install"
110
111 Automation/integration notes:
112         The first time a given machine runs "build" or "testinstall" (and
113         any time the -rebuild-image is used), new docker images are built,
114         which is quite slow. If you use on-demand VMs to run automated builds,
115         run "build" and "testinstall" once when setting up your initial VM
116         image, and be prepared to rebuild that VM image when package-building
117         slows down (this will happen when new dependencies are introduced).
118
119         The "build" subcommand, if successful, also runs
120         dpkg-scanpackages to create/replace Packages.gz in the package
121         dir. This enables the "testinstall" subcommand to list the
122         package dir as a source in /etc/apt/sources.*.
123
124 Options:
125 `)
126                 flags.PrintDefaults()
127         }
128         if ok, code := cmd.ParseFlags(flags, prog, args, "", stderr); !ok {
129                 return opts, false, code
130         }
131         if opts.SourceDir == "" {
132                 d, err := os.Getwd()
133                 if err != nil {
134                         fmt.Fprintf(stderr, "error getting current working directory: %s\n", err)
135                         return opts, false, 1
136                 }
137                 opts.SourceDir = d
138         }
139         opts.PackageDir = filepath.Clean(opts.PackageDir)
140         abs, err := filepath.Abs(opts.SourceDir)
141         if err != nil {
142                 fmt.Fprintf(stderr, "error resolving source dir %q: %s\n", opts.SourceDir, err)
143                 return opts, false, 1
144         }
145         opts.SourceDir = abs
146         return opts, true, 0
147 }