github.com/thanos-io/thanos@v0.32.5/scripts/cfggen/main.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package main
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  	"reflect"
    12  	"strings"
    13  
    14  	"github.com/fatih/structtag"
    15  	"github.com/go-kit/log"
    16  	"github.com/go-kit/log/level"
    17  	"github.com/pkg/errors"
    18  	"github.com/thanos-io/objstore/client"
    19  	"github.com/thanos-io/objstore/providers/azure"
    20  	"github.com/thanos-io/objstore/providers/bos"
    21  	"github.com/thanos-io/objstore/providers/cos"
    22  	"github.com/thanos-io/objstore/providers/filesystem"
    23  	"github.com/thanos-io/objstore/providers/gcs"
    24  	"github.com/thanos-io/objstore/providers/oss"
    25  	"github.com/thanos-io/objstore/providers/s3"
    26  	"github.com/thanos-io/objstore/providers/swift"
    27  	"gopkg.in/alecthomas/kingpin.v2"
    28  	"gopkg.in/yaml.v2"
    29  
    30  	"github.com/thanos-io/thanos/pkg/httpconfig"
    31  
    32  	"github.com/thanos-io/thanos/pkg/alert"
    33  	"github.com/thanos-io/thanos/pkg/cacheutil"
    34  	"github.com/thanos-io/thanos/pkg/logging"
    35  	"github.com/thanos-io/thanos/pkg/queryfrontend"
    36  	storecache "github.com/thanos-io/thanos/pkg/store/cache"
    37  	trclient "github.com/thanos-io/thanos/pkg/tracing/client"
    38  	"github.com/thanos-io/thanos/pkg/tracing/elasticapm"
    39  	"github.com/thanos-io/thanos/pkg/tracing/google_cloud"
    40  	"github.com/thanos-io/thanos/pkg/tracing/jaeger"
    41  	"github.com/thanos-io/thanos/pkg/tracing/lightstep"
    42  	"github.com/thanos-io/thanos/pkg/tracing/otlp"
    43  )
    44  
    45  var (
    46  	configs        map[string]interface{}
    47  	possibleValues []string
    48  
    49  	bucketConfigs = map[client.ObjProvider]interface{}{
    50  		client.AZURE:      azure.Config{},
    51  		client.GCS:        gcs.Config{},
    52  		client.S3:         s3.DefaultConfig,
    53  		client.SWIFT:      swift.DefaultConfig,
    54  		client.COS:        cos.DefaultConfig,
    55  		client.ALIYUNOSS:  oss.Config{},
    56  		client.FILESYSTEM: filesystem.Config{},
    57  		client.BOS:        bos.Config{},
    58  	}
    59  
    60  	tracingConfigs = map[trclient.TracingProvider]interface{}{
    61  		trclient.OpenTelemetryProtocol: otlp.Config{},
    62  		trclient.Jaeger:                jaeger.Config{},
    63  		trclient.GoogleCloud:           google_cloud.Config{},
    64  		trclient.ElasticAPM:            elasticapm.Config{},
    65  		trclient.Lightstep:             lightstep.Config{},
    66  	}
    67  	indexCacheConfigs = map[storecache.IndexCacheProvider]interface{}{
    68  		storecache.INMEMORY:  storecache.InMemoryIndexCacheConfig{},
    69  		storecache.MEMCACHED: cacheutil.MemcachedClientConfig{},
    70  		storecache.REDIS:     cacheutil.DefaultRedisClientConfig,
    71  	}
    72  
    73  	queryfrontendCacheConfigs = map[queryfrontend.ResponseCacheProvider]interface{}{
    74  		queryfrontend.INMEMORY:  queryfrontend.InMemoryResponseCacheConfig{},
    75  		queryfrontend.MEMCACHED: queryfrontend.MemcachedResponseCacheConfig{},
    76  		queryfrontend.REDIS:     queryfrontend.DefaultRedisConfig,
    77  	}
    78  )
    79  
    80  func init() {
    81  	configs = map[string]interface{}{}
    82  	configs[name(logging.RequestConfig{})] = logging.RequestConfig{}
    83  
    84  	alertmgrCfg := alert.DefaultAlertmanagerConfig()
    85  	alertmgrCfg.EndpointsConfig.FileSDConfigs = []httpconfig.FileSDConfig{{}}
    86  	configs[name(alert.AlertingConfig{})] = alert.AlertingConfig{Alertmanagers: []alert.AlertmanagerConfig{alertmgrCfg}}
    87  
    88  	queryCfg := httpconfig.DefaultConfig()
    89  	queryCfg.EndpointsConfig.FileSDConfigs = []httpconfig.FileSDConfig{{}}
    90  	configs[name(httpconfig.Config{})] = []httpconfig.Config{queryCfg}
    91  
    92  	for typ, config := range bucketConfigs {
    93  		configs[name(config)] = client.BucketConfig{Type: typ, Config: config}
    94  	}
    95  	for typ, config := range tracingConfigs {
    96  		configs[name(config)] = trclient.TracingConfig{Type: typ, Config: config}
    97  	}
    98  	for typ, config := range indexCacheConfigs {
    99  		configs[name(config)] = storecache.IndexCacheConfig{Type: typ, Config: config}
   100  	}
   101  	for typ, config := range queryfrontendCacheConfigs {
   102  		configs[name(config)] = queryfrontend.CacheProviderConfig{Type: typ, Config: config}
   103  	}
   104  
   105  	for k := range configs {
   106  		possibleValues = append(possibleValues, k)
   107  	}
   108  }
   109  
   110  func name(typ interface{}) string {
   111  	return fmt.Sprintf("%T", typ)
   112  }
   113  
   114  func main() {
   115  	app := kingpin.New(filepath.Base(os.Args[0]), "Thanos config examples generator.")
   116  	app.HelpFlag.Short('h')
   117  	structName := app.Flag("name", fmt.Sprintf("Name of the struct to generated example for. Possible values: %v", strings.Join(possibleValues, ","))).Required().String()
   118  
   119  	errLogger := level.Error(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)))
   120  	if _, err := app.Parse(os.Args[1:]); err != nil {
   121  		errLogger.Log("err", err)
   122  		os.Exit(1)
   123  	}
   124  
   125  	if c, ok := configs[*structName]; ok {
   126  		if err := generate(c, os.Stdout); err != nil {
   127  			errLogger.Log("err", err)
   128  			os.Exit(1)
   129  		}
   130  		return
   131  	}
   132  
   133  	errLogger.Log("err", errors.Errorf("%v struct not found. Possible values %v", *structName, strings.Join(possibleValues, ",")))
   134  	os.Exit(1)
   135  }
   136  
   137  func generate(obj interface{}, w io.Writer) error {
   138  	// We forbid omitempty option. This is for simplification for doc generation.
   139  	if err := checkForOmitEmptyTagOption(obj); err != nil {
   140  		return errors.Wrap(err, "invalid type")
   141  	}
   142  	return yaml.NewEncoder(w).Encode(obj)
   143  }
   144  
   145  func checkForOmitEmptyTagOption(obj interface{}) error {
   146  	return checkForOmitEmptyTagOptionRec(reflect.ValueOf(obj))
   147  }
   148  
   149  func checkForOmitEmptyTagOptionRec(v reflect.Value) error {
   150  	switch v.Kind() {
   151  	case reflect.Struct:
   152  		for i := 0; i < v.NumField(); i++ {
   153  			tags, err := structtag.Parse(string(v.Type().Field(i).Tag))
   154  			if err != nil {
   155  				return errors.Wrapf(err, "%s: failed to parse tag %q", v.Type().Field(i).Name, v.Type().Field(i).Tag)
   156  			}
   157  
   158  			tag, err := tags.Get("yaml")
   159  			if err != nil {
   160  				return errors.Wrapf(err, "%s: failed to get tag %q", v.Type().Field(i).Name, v.Type().Field(i).Tag)
   161  			}
   162  
   163  			for _, opts := range tag.Options {
   164  				if opts == "omitempty" {
   165  					return errors.Errorf("omitempty is forbidden for config, but spotted on field '%s'", v.Type().Field(i).Name)
   166  				}
   167  			}
   168  
   169  			if err := checkForOmitEmptyTagOptionRec(v.Field(i)); err != nil {
   170  				return errors.Wrapf(err, "%s", v.Type().Field(i).Name)
   171  			}
   172  		}
   173  
   174  	case reflect.Ptr:
   175  		return errors.New("nil pointers are not allowed in configuration")
   176  
   177  	case reflect.Interface:
   178  		return checkForOmitEmptyTagOptionRec(v.Elem())
   179  	}
   180  
   181  	return nil
   182  }