github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/admin-prometheus-generate.go (about) 1 // Copyright (c) 2015-2022 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "net/url" 22 "time" 23 24 "github.com/fatih/color" 25 "github.com/minio/cli" 26 "github.com/minio/mc/pkg/probe" 27 "github.com/minio/pkg/v2/console" 28 29 json "github.com/minio/colorjson" 30 yaml "gopkg.in/yaml.v2" 31 ) 32 33 const ( 34 defaultJobName = "minio-job" 35 nodeJobName = "minio-job-node" 36 bucketJobName = "minio-job-bucket" 37 defaultMetricsPath = "/minio/v2/metrics/cluster" 38 nodeMetricsPath = "/minio/v2/metrics/node" 39 bucketMetricsPath = "/minio/v2/metrics/bucket" 40 resourceJobName = "minio-job-resource" 41 resourceMetricsPath = "/minio/v2/metrics/resource" 42 ) 43 44 var prometheusFlags = []cli.Flag{ 45 cli.BoolFlag{ 46 Name: "public", 47 Usage: "disable bearer token generation for scrape_configs", 48 }, 49 } 50 51 var adminPrometheusGenerateCmd = cli.Command{ 52 Name: "generate", 53 Usage: "generates prometheus config", 54 Action: mainAdminPrometheusGenerate, 55 OnUsageError: onUsageError, 56 Before: setGlobalsFromContext, 57 Flags: append(prometheusFlags, globalFlags...), 58 HideHelpCommand: true, 59 CustomHelpTemplate: `NAME: 60 {{.HelpName}} - {{.Usage}} 61 62 USAGE: 63 {{.HelpName}} TARGET [METRIC-TYPE] 64 65 METRIC-TYPE: 66 valid values are ['cluster', 'node', 'bucket']. Defaults to 'cluster' if not specified. 67 68 FLAGS: 69 {{range .VisibleFlags}}{{.}} 70 {{end}} 71 EXAMPLES: 72 1. Generate a default prometheus config. 73 {{.Prompt}} {{.HelpName}} myminio 74 75 2. Generate prometheus config for node metrics. 76 {{.Prompt}} {{.HelpName}} play node 77 78 3. Generate prometheus config for bucket metrics. 79 {{.Prompt}} {{.HelpName}} play bucket 80 81 4. Generate prometheus config for resource metrics. 82 {{.Prompt}} {{.HelpName}} play resource 83 `, 84 } 85 86 // PrometheusConfig - container to hold the top level scrape config. 87 type PrometheusConfig struct { 88 ScrapeConfigs []ScrapeConfig `yaml:"scrape_configs,omitempty"` 89 } 90 91 // String colorized prometheus config yaml. 92 func (c PrometheusConfig) String() string { 93 b, e := yaml.Marshal(c) 94 fatalIf(probe.NewError(e), "Unable to generate Prometheus config") 95 96 return console.Colorize("yaml", string(b)) 97 } 98 99 // JSON jsonified prometheus config. 100 func (c PrometheusConfig) JSON() string { 101 jsonMessageBytes, e := json.MarshalIndent(c.ScrapeConfigs[0], "", " ") 102 fatalIf(probe.NewError(e), "Unable to marshal into JSON.") 103 return string(jsonMessageBytes) 104 } 105 106 // StatConfig - container to hold the targets config. 107 type StatConfig struct { 108 Targets []string `yaml:",flow" json:"targets"` 109 } 110 111 // String colorized stat config yaml. 112 func (t StatConfig) String() string { 113 b, e := yaml.Marshal(t) 114 fatalIf(probe.NewError(e), "Unable to generate Prometheus config") 115 116 return console.Colorize("yaml", string(b)) 117 } 118 119 // JSON jsonified stat config. 120 func (t StatConfig) JSON() string { 121 jsonMessageBytes, e := json.MarshalIndent(t.Targets, "", " ") 122 fatalIf(probe.NewError(e), "Unable to marshal into JSON.") 123 return string(jsonMessageBytes) 124 } 125 126 // ScrapeConfig configures a scraping unit for Prometheus. 127 type ScrapeConfig struct { 128 JobName string `yaml:"job_name" json:"jobName"` 129 BearerToken string `yaml:"bearer_token,omitempty" json:"bearerToken,omitempty"` 130 MetricsPath string `yaml:"metrics_path,omitempty" json:"metricsPath"` 131 Scheme string `yaml:"scheme,omitempty" json:"scheme"` 132 StaticConfigs []StatConfig `yaml:"static_configs,omitempty" json:"staticConfigs"` 133 } 134 135 const ( 136 defaultPrometheusJWTExpiry = 100 * 365 * 24 * time.Hour 137 ) 138 139 // checkAdminPrometheusSyntax - validate all the passed arguments 140 func checkAdminPrometheusSyntax(ctx *cli.Context) { 141 if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 { 142 showCommandHelpAndExit(ctx, 1) // last argument is exit code 143 } 144 } 145 146 func generatePrometheusConfig(ctx *cli.Context) error { 147 // Get the alias parameter from cli 148 args := ctx.Args() 149 alias := cleanAlias(args.Get(0)) 150 151 if !isValidAlias(alias) { 152 fatalIf(errInvalidAlias(alias), "Invalid alias.") 153 } 154 155 hostConfig := mustGetHostConfig(alias) 156 if hostConfig == nil { 157 fatalIf(errInvalidAliasedURL(alias), "No such alias `"+alias+"` found.") 158 return nil 159 } 160 161 u, e := url.Parse(hostConfig.URL) 162 if e != nil { 163 return e 164 } 165 166 metricsSubSystem := args.Get(1) 167 var config PrometheusConfig 168 switch metricsSubSystem { 169 case "node": 170 config = PrometheusConfig{ 171 ScrapeConfigs: []ScrapeConfig{ 172 { 173 JobName: nodeJobName, 174 MetricsPath: nodeMetricsPath, 175 StaticConfigs: []StatConfig{ 176 { 177 Targets: []string{""}, 178 }, 179 }, 180 }, 181 }, 182 } 183 case "bucket": 184 config = PrometheusConfig{ 185 ScrapeConfigs: []ScrapeConfig{ 186 { 187 JobName: bucketJobName, 188 MetricsPath: bucketMetricsPath, 189 StaticConfigs: []StatConfig{ 190 { 191 Targets: []string{""}, 192 }, 193 }, 194 }, 195 }, 196 } 197 case "resource": 198 config = PrometheusConfig{ 199 ScrapeConfigs: []ScrapeConfig{ 200 { 201 JobName: resourceJobName, 202 MetricsPath: resourceMetricsPath, 203 StaticConfigs: []StatConfig{ 204 { 205 Targets: []string{""}, 206 }, 207 }, 208 }, 209 }, 210 } 211 case "", "cluster": 212 config = PrometheusConfig{ 213 ScrapeConfigs: []ScrapeConfig{ 214 { 215 JobName: defaultJobName, 216 MetricsPath: defaultMetricsPath, 217 StaticConfigs: []StatConfig{ 218 { 219 Targets: []string{""}, 220 }, 221 }, 222 }, 223 }, 224 } 225 default: 226 fatalIf(errInvalidArgument().Trace(), "invalid metric type '%v'", metricsSubSystem) 227 } 228 229 if !ctx.Bool("public") { 230 token, e := getPrometheusToken(hostConfig) 231 if e != nil { 232 return e 233 } 234 // Setting the values 235 config.ScrapeConfigs[0].BearerToken = token 236 } 237 config.ScrapeConfigs[0].Scheme = u.Scheme 238 config.ScrapeConfigs[0].StaticConfigs[0].Targets[0] = u.Host 239 240 printMsg(config) 241 242 return nil 243 } 244 245 // mainAdminPrometheus is the handle for "mc admin prometheus generate" sub-command. 246 func mainAdminPrometheusGenerate(ctx *cli.Context) error { 247 console.SetColor("yaml", color.New(color.FgGreen)) 248 249 checkAdminPrometheusSyntax(ctx) 250 251 if err := generatePrometheusConfig(ctx); err != nil { 252 return nil 253 } 254 255 return nil 256 }