mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-03 18:53:13 -05:00 
			
		
		
		
	
		
			
	
	
		
			173 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			173 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package test
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								import (
							 | 
						|||
| 
								 | 
							
									"bytes"
							 | 
						|||
| 
								 | 
							
									"go/ast"
							 | 
						|||
| 
								 | 
							
									"go/parser"
							 | 
						|||
| 
								 | 
							
									"go/token"
							 | 
						|||
| 
								 | 
							
									"os"
							 | 
						|||
| 
								 | 
							
									"path/filepath"
							 | 
						|||
| 
								 | 
							
									"strings"
							 | 
						|||
| 
								 | 
							
									"testing"
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									"github.com/coredns/coredns/plugin"
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									dto "github.com/prometheus/client_model/go"
							 | 
						|||
| 
								 | 
							
									"github.com/prometheus/common/expfmt"
							 | 
						|||
| 
								 | 
							
									"github.com/prometheus/prometheus/util/promlint"
							 | 
						|||
| 
								 | 
							
								)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								func TestMetricNaming(t *testing.T) {
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									walker := validMetricWalker{}
							 | 
						|||
| 
								 | 
							
									err := filepath.Walk("..", walker.walk)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if err != nil {
							 | 
						|||
| 
								 | 
							
										t.Fatal(err)
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if len(walker.Metrics) > 0 {
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										buf := &bytes.Buffer{}
							 | 
						|||
| 
								 | 
							
										encoder := expfmt.NewEncoder(buf, expfmt.FmtText)
							 | 
						|||
| 
								 | 
							
										for _, mf := range walker.Metrics {
							 | 
						|||
| 
								 | 
							
											if err := encoder.Encode(mf); err != nil {
							 | 
						|||
| 
								 | 
							
												t.Fatalf("Encoding and sending metric family: %s", err)
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										l := promlint.New(buf)
							 | 
						|||
| 
								 | 
							
										problems, err := l.Lint()
							 | 
						|||
| 
								 | 
							
										if err != nil {
							 | 
						|||
| 
								 | 
							
											t.Fatalf("Link found error: %s", err)
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if len(problems) > 0 {
							 | 
						|||
| 
								 | 
							
											t.Fatalf("A slice of Problems indicating any issues found in the metrics stream: %s", problems)
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								type validMetricWalker struct {
							 | 
						|||
| 
								 | 
							
									Metrics []*dto.MetricFamily
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								func (w *validMetricWalker) walk(path string, info os.FileInfo, _ error) error {
							 | 
						|||
| 
								 | 
							
									// only for regular files, not starting with a . and those that are go files.
							 | 
						|||
| 
								 | 
							
									if !info.Mode().IsRegular() {
							 | 
						|||
| 
								 | 
							
										return nil
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									// Is it appropriate to compare the file name equals metrics.go directly?
							 | 
						|||
| 
								 | 
							
									if strings.HasPrefix(path, "../.") {
							 | 
						|||
| 
								 | 
							
										return nil
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									if strings.HasSuffix(path, "_test.go") {
							 | 
						|||
| 
								 | 
							
										return nil
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									if !strings.HasSuffix(path, ".go") {
							 | 
						|||
| 
								 | 
							
										return nil
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									fs := token.NewFileSet()
							 | 
						|||
| 
								 | 
							
									f, err := parser.ParseFile(fs, path, nil, parser.AllErrors)
							 | 
						|||
| 
								 | 
							
									if err != nil {
							 | 
						|||
| 
								 | 
							
										return err
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									l := &metric{}
							 | 
						|||
| 
								 | 
							
									ast.Walk(l, f)
							 | 
						|||
| 
								 | 
							
									if l.Metric != nil {
							 | 
						|||
| 
								 | 
							
										w.Metrics = append(w.Metrics, l.Metric)
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									return nil
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								type metric struct {
							 | 
						|||
| 
								 | 
							
									Metric *dto.MetricFamily
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								func (l metric) Visit(n ast.Node) ast.Visitor {
							 | 
						|||
| 
								 | 
							
									if n == nil {
							 | 
						|||
| 
								 | 
							
										return nil
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									ce, ok := n.(*ast.CallExpr)
							 | 
						|||
| 
								 | 
							
									if !ok {
							 | 
						|||
| 
								 | 
							
										return l
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									se, ok := ce.Fun.(*ast.SelectorExpr)
							 | 
						|||
| 
								 | 
							
									if !ok {
							 | 
						|||
| 
								 | 
							
										return l
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									id, ok := se.X.(*ast.Ident)
							 | 
						|||
| 
								 | 
							
									if !ok {
							 | 
						|||
| 
								 | 
							
										return l
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									if id.Name != "prometheus" { //prometheus
							 | 
						|||
| 
								 | 
							
										return l
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									var metricsType dto.MetricType
							 | 
						|||
| 
								 | 
							
									switch se.Sel.Name {
							 | 
						|||
| 
								 | 
							
									case "NewCounterVec", "NewCounter":
							 | 
						|||
| 
								 | 
							
										metricsType = dto.MetricType_COUNTER
							 | 
						|||
| 
								 | 
							
									case "NewGaugeVec", "NewGauge":
							 | 
						|||
| 
								 | 
							
										metricsType = dto.MetricType_GAUGE
							 | 
						|||
| 
								 | 
							
									case "NewHistogramVec", "NewHistogram":
							 | 
						|||
| 
								 | 
							
										metricsType = dto.MetricType_HISTOGRAM
							 | 
						|||
| 
								 | 
							
									case "NewSummaryVec", "NewSummary":
							 | 
						|||
| 
								 | 
							
										metricsType = dto.MetricType_SUMMARY
							 | 
						|||
| 
								 | 
							
									default:
							 | 
						|||
| 
								 | 
							
										return l
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									// Check first arg, that should have basic lit with capital
							 | 
						|||
| 
								 | 
							
									if len(ce.Args) < 1 {
							 | 
						|||
| 
								 | 
							
										return l
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									bl, ok := ce.Args[0].(*ast.CompositeLit)
							 | 
						|||
| 
								 | 
							
									if !ok {
							 | 
						|||
| 
								 | 
							
										return l
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// parse Namespace Subsystem Name Help
							 | 
						|||
| 
								 | 
							
									var subsystem, name, help string
							 | 
						|||
| 
								 | 
							
									for _, elt := range bl.Elts {
							 | 
						|||
| 
								 | 
							
										expr, ok := elt.(*ast.KeyValueExpr)
							 | 
						|||
| 
								 | 
							
										if !ok {
							 | 
						|||
| 
								 | 
							
											continue
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										object, ok := expr.Key.(*ast.Ident)
							 | 
						|||
| 
								 | 
							
										if !ok {
							 | 
						|||
| 
								 | 
							
											continue
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										value, ok := expr.Value.(*ast.BasicLit)
							 | 
						|||
| 
								 | 
							
										if !ok {
							 | 
						|||
| 
								 | 
							
											continue
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										switch object.Name {
							 | 
						|||
| 
								 | 
							
										case "Subsystem":
							 | 
						|||
| 
								 | 
							
											subsystem = value.Value
							 | 
						|||
| 
								 | 
							
										case "Name":
							 | 
						|||
| 
								 | 
							
											name = value.Value
							 | 
						|||
| 
								 | 
							
										case "Help":
							 | 
						|||
| 
								 | 
							
											help = value.Value
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// validate metrics field
							 | 
						|||
| 
								 | 
							
									if len(name) == 0 || len(help) == 0 {
							 | 
						|||
| 
								 | 
							
										return l
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									var metricName string
							 | 
						|||
| 
								 | 
							
									if len(subsystem) > 0 {
							 | 
						|||
| 
								 | 
							
										metricName = strings.Join([]string{plugin.Namespace, subsystem, name}, "_")
							 | 
						|||
| 
								 | 
							
									} else {
							 | 
						|||
| 
								 | 
							
										metricName = strings.Join([]string{plugin.Namespace, name}, "_")
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									l.Metric = &dto.MetricFamily{
							 | 
						|||
| 
								 | 
							
										Name: &metricName,
							 | 
						|||
| 
								 | 
							
										Help: &help,
							 | 
						|||
| 
								 | 
							
										Type: &metricsType,
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									return l
							 | 
						|||
| 
								 | 
							
								}
							 |