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  }