Merge branch '15000-config-api'
[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         "flag"
10         "fmt"
11         "io"
12         "io/ioutil"
13         "os"
14         "os/exec"
15
16         "git.curoverse.com/arvados.git/sdk/go/arvados"
17         "git.curoverse.com/arvados.git/sdk/go/ctxlog"
18         "github.com/ghodss/yaml"
19 )
20
21 var DumpCommand dumpCommand
22
23 type dumpCommand struct{}
24
25 func (dumpCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
26         var err error
27         defer func() {
28                 if err != nil {
29                         fmt.Fprintf(stderr, "%s\n", err)
30                 }
31         }()
32
33         flags := flag.NewFlagSet("", flag.ContinueOnError)
34         flags.SetOutput(stderr)
35         configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
36         err = flags.Parse(args)
37         if err == flag.ErrHelp {
38                 err = nil
39                 return 0
40         } else if err != nil {
41                 return 2
42         }
43
44         if len(flags.Args()) != 0 {
45                 flags.Usage()
46                 return 2
47         }
48         log := ctxlog.New(stderr, "text", "info")
49         cfg, err := loadFileOrStdin(*configFile, stdin, log)
50         if err != nil {
51                 return 1
52         }
53         out, err := yaml.Marshal(cfg)
54         if err != nil {
55                 return 1
56         }
57         _, err = stdout.Write(out)
58         if err != nil {
59                 return 1
60         }
61         return 0
62 }
63
64 var CheckCommand checkCommand
65
66 type checkCommand struct{}
67
68 func (checkCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
69         var err error
70         defer func() {
71                 if err != nil {
72                         fmt.Fprintf(stderr, "%s\n", err)
73                 }
74         }()
75
76         flags := flag.NewFlagSet("", flag.ContinueOnError)
77         flags.SetOutput(stderr)
78         configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
79         err = flags.Parse(args)
80         if err == flag.ErrHelp {
81                 err = nil
82                 return 0
83         } else if err != nil {
84                 return 2
85         }
86
87         if len(flags.Args()) != 0 {
88                 flags.Usage()
89                 return 2
90         }
91         log := &plainLogger{w: stderr}
92         var buf []byte
93         if *configFile == "-" {
94                 buf, err = ioutil.ReadAll(stdin)
95         } else {
96                 buf, err = ioutil.ReadFile(*configFile)
97         }
98         if err != nil {
99                 return 1
100         }
101         withoutDepr, err := load(bytes.NewBuffer(buf), log, false)
102         if err != nil {
103                 return 1
104         }
105         withDepr, err := load(bytes.NewBuffer(buf), nil, true)
106         if err != nil {
107                 return 1
108         }
109         cmd := exec.Command("diff", "-u", "--label", "without-deprecated-configs", "--label", "relying-on-deprecated-configs", "/dev/fd/3", "/dev/fd/4")
110         for _, obj := range []interface{}{withoutDepr, withDepr} {
111                 y, _ := yaml.Marshal(obj)
112                 pr, pw, err := os.Pipe()
113                 if err != nil {
114                         return 1
115                 }
116                 defer pr.Close()
117                 go func() {
118                         io.Copy(pw, bytes.NewBuffer(y))
119                         pw.Close()
120                 }()
121                 cmd.ExtraFiles = append(cmd.ExtraFiles, pr)
122         }
123         diff, err := cmd.CombinedOutput()
124         if bytes.HasPrefix(diff, []byte("--- ")) {
125                 fmt.Fprintln(stdout, "Your configuration is relying on deprecated entries. Suggest making the following changes.")
126                 stdout.Write(diff)
127                 return 1
128         } else if len(diff) > 0 {
129                 fmt.Fprintf(stderr, "Unexpected diff output:\n%s", diff)
130                 return 1
131         } else if err != nil {
132                 return 1
133         }
134         if log.used {
135                 return 1
136         }
137         return 0
138 }
139
140 type plainLogger struct {
141         w    io.Writer
142         used bool
143 }
144
145 func (pl *plainLogger) Warnf(format string, args ...interface{}) {
146         pl.used = true
147         fmt.Fprintf(pl.w, format+"\n", args...)
148 }