1
2
3
4
5
6
7
8
9
10
11
12
13
14 package cli
15
16 import (
17 "fmt"
18 "net/url"
19 "os"
20 "path"
21 "time"
22
23 "github.com/go-openapi/strfmt"
24 promconfig "github.com/prometheus/common/config"
25 "github.com/prometheus/common/version"
26 "golang.org/x/mod/semver"
27 "gopkg.in/alecthomas/kingpin.v2"
28
29 "github.com/prometheus/alertmanager/api/v2/client"
30 "github.com/prometheus/alertmanager/cli/config"
31 "github.com/prometheus/alertmanager/cli/format"
32
33 clientruntime "github.com/go-openapi/runtime/client"
34 )
35
36 var (
37 verbose bool
38 alertmanagerURL *url.URL
39 output string
40 timeout time.Duration
41 httpConfigFile string
42 versionCheck bool
43
44 configFiles = []string{os.ExpandEnv("$HOME/.config/amtool/config.yml"), "/etc/amtool/config.yml"}
45 legacyFlags = map[string]string{"comment_required": "require-comment"}
46 )
47
48 func requireAlertManagerURL(pc *kingpin.ParseContext) error {
49
50 for _, elem := range pc.Elements {
51 f, ok := elem.Clause.(*kingpin.FlagClause)
52 if !ok {
53 continue
54 }
55 name := f.Model().Name
56 if name == "help" || name == "help-long" || name == "help-man" {
57 return nil
58 }
59 }
60 if alertmanagerURL == nil {
61 kingpin.Fatalf("required flag --alertmanager.url not provided")
62 }
63 return nil
64 }
65
66 const (
67 defaultAmHost = "localhost"
68 defaultAmPort = "9093"
69 defaultAmApiv2path = "/api/v2"
70 )
71
72
73 func NewAlertmanagerClient(amURL *url.URL) *client.AlertmanagerAPI {
74 address := defaultAmHost + ":" + defaultAmPort
75 schemes := []string{"http"}
76
77 if amURL.Host != "" {
78 address = amURL.Host
79 }
80 if amURL.Scheme != "" {
81 schemes = []string{amURL.Scheme}
82 }
83
84 cr := clientruntime.New(address, path.Join(amURL.Path, defaultAmApiv2path), schemes)
85
86 if amURL.User != nil && httpConfigFile != "" {
87 kingpin.Fatalf("basic authentication and http.config.file are mutually exclusive")
88 }
89
90 if amURL.User != nil {
91 password, _ := amURL.User.Password()
92 cr.DefaultAuthentication = clientruntime.BasicAuth(amURL.User.Username(), password)
93 }
94
95 if httpConfigFile != "" {
96 var err error
97 httpConfig, _, err := promconfig.LoadHTTPConfigFile(httpConfigFile)
98 if err != nil {
99 kingpin.Fatalf("failed to load HTTP config file: %v", err)
100 }
101
102 httpclient, err := promconfig.NewClientFromConfig(*httpConfig, "amtool")
103 if err != nil {
104 kingpin.Fatalf("failed to create a new HTTP client: %v", err)
105 }
106 cr = clientruntime.NewWithClient(address, path.Join(amURL.Path, defaultAmApiv2path), schemes, httpclient)
107 }
108
109 c := client.New(cr, strfmt.Default)
110
111 if !versionCheck {
112 return c
113 }
114
115 status, err := c.General.GetStatus(nil)
116 if err != nil || status.Payload.VersionInfo == nil || version.Version == "" {
117
118 return c
119 }
120
121 if semver.MajorMinor("v"+*status.Payload.VersionInfo.Version) != semver.MajorMinor("v"+version.Version) {
122 fmt.Fprintf(os.Stderr, "Warning: amtool version (%s) and alertmanager version (%s) are different.\n", version.Version, *status.Payload.VersionInfo.Version)
123 }
124
125 return c
126 }
127
128
129 func Execute() {
130 app := kingpin.New("amtool", helpRoot).UsageWriter(os.Stdout)
131
132 format.InitFormatFlags(app)
133
134 app.Flag("verbose", "Verbose running information").Short('v').BoolVar(&verbose)
135 app.Flag("alertmanager.url", "Alertmanager to talk to").URLVar(&alertmanagerURL)
136 app.Flag("output", "Output formatter (simple, extended, json)").Short('o').Default("simple").EnumVar(&output, "simple", "extended", "json")
137 app.Flag("timeout", "Timeout for the executed command").Default("30s").DurationVar(&timeout)
138 app.Flag("http.config.file", "HTTP client configuration file for amtool to connect to Alertmanager.").PlaceHolder("<filename>").ExistingFileVar(&httpConfigFile)
139 app.Flag("version-check", "Check alertmanager version. Use --no-version-check to disable.").Default("true").BoolVar(&versionCheck)
140
141 app.Version(version.Print("amtool"))
142 app.GetFlag("help").Short('h')
143 app.UsageTemplate(kingpin.CompactUsageTemplate)
144
145 resolver, err := config.NewResolver(configFiles, legacyFlags)
146 if err != nil {
147 kingpin.Fatalf("could not load config file: %v\n", err)
148 }
149
150 configureAlertCmd(app)
151 configureSilenceCmd(app)
152 configureCheckConfigCmd(app)
153 configureClusterCmd(app)
154 configureConfigCmd(app)
155 configureTemplateCmd(app)
156
157 err = resolver.Bind(app, os.Args[1:])
158 if err != nil {
159 kingpin.Fatalf("%v\n", err)
160 }
161
162 _, err = app.Parse(os.Args[1:])
163 if err != nil {
164 kingpin.Fatalf("%v\n", err)
165 }
166 }
167
168 const (
169 helpRoot = `View and modify the current Alertmanager state.
170
171 Config File:
172 The alertmanager tool will read a config file in YAML format from one of two
173 default config locations: $HOME/.config/amtool/config.yml or
174 /etc/amtool/config.yml
175
176 All flags can be given in the config file, but the following are the suited for
177 static configuration:
178
179 alertmanager.url
180 Set a default alertmanager url for each request
181
182 author
183 Set a default author value for new silences. If this argument is not
184 specified then the username will be used
185
186 require-comment
187 Bool, whether to require a comment on silence creation. Defaults to true
188
189 output
190 Set a default output type. Options are (simple, extended, json)
191
192 date.format
193 Sets the output format for dates. Defaults to "2006-01-02 15:04:05 MST"
194
195 http.config.file
196 HTTP client configuration file for amtool to connect to Alertmanager.
197 The format is https://prometheus.io/docs/alerting/latest/configuration/#http_config.
198 `
199 )
200
View as plain text