15000: Recommend checking config publication errors during install.
[arvados.git] / lib / config / cmd.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package config
6
7 import (
8         "bytes"
9         "fmt"
10         "io"
11         "io/ioutil"
12         "os"
13         "os/exec"
14
15         "git.curoverse.com/arvados.git/sdk/go/ctxlog"
16         "github.com/ghodss/yaml"
17 )
18
19 var DumpCommand dumpCommand
20
21 type dumpCommand struct{}
22
23 func (dumpCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
24         var err error
25         defer func() {
26                 if err != nil {
27                         fmt.Fprintf(stderr, "%s\n", err)
28                 }
29         }()
30         if len(args) != 0 {
31                 err = fmt.Errorf("usage: %s <config-src.yaml >config-min.yaml", prog)
32                 return 2
33         }
34         log := ctxlog.New(stderr, "text", "info")
35         cfg, err := Load(stdin, log)
36         if err != nil {
37                 return 1
38         }
39         out, err := yaml.Marshal(cfg)
40         if err != nil {
41                 return 1
42         }
43         _, err = stdout.Write(out)
44         if err != nil {
45                 return 1
46         }
47         return 0
48 }
49
50 var CheckCommand checkCommand
51
52 type checkCommand struct{}
53
54 func (checkCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
55         var err error
56         defer func() {
57                 if err != nil {
58                         fmt.Fprintf(stderr, "%s\n", err)
59                 }
60         }()
61         if len(args) != 0 {
62                 err = fmt.Errorf("usage: %s <config-src.yaml && echo 'no changes needed'", prog)
63                 return 2
64         }
65         log := &plainLogger{w: stderr}
66         buf, err := ioutil.ReadAll(stdin)
67         if err != nil {
68                 return 1
69         }
70         withoutDepr, err := load(bytes.NewBuffer(buf), log, false)
71         if err != nil {
72                 return 1
73         }
74         withDepr, err := load(bytes.NewBuffer(buf), nil, true)
75         if err != nil {
76                 return 1
77         }
78         cmd := exec.Command("diff", "-u", "--label", "without-deprecated-configs", "--label", "relying-on-deprecated-configs", "/dev/fd/3", "/dev/fd/4")
79         for _, obj := range []interface{}{withoutDepr, withDepr} {
80                 y, _ := yaml.Marshal(obj)
81                 pr, pw, err := os.Pipe()
82                 if err != nil {
83                         return 1
84                 }
85                 defer pr.Close()
86                 go func() {
87                         io.Copy(pw, bytes.NewBuffer(y))
88                         pw.Close()
89                 }()
90                 cmd.ExtraFiles = append(cmd.ExtraFiles, pr)
91         }
92         diff, err := cmd.CombinedOutput()
93         if bytes.HasPrefix(diff, []byte("--- ")) {
94                 fmt.Fprintln(stdout, "Your configuration is relying on deprecated entries. Suggest making the following changes.")
95                 stdout.Write(diff)
96                 return 1
97         } else if len(diff) > 0 {
98                 fmt.Fprintf(stderr, "Unexpected diff output:\n%s", diff)
99                 return 1
100         } else if err != nil {
101                 return 1
102         }
103         if log.used {
104                 return 1
105         }
106         return 0
107 }
108
109 type plainLogger struct {
110         w    io.Writer
111         used bool
112 }
113
114 func (pl *plainLogger) Warnf(format string, args ...interface{}) {
115         pl.used = true
116         fmt.Fprintf(pl.w, format+"\n", args...)
117 }