You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	Remove internal/semconv-gen (#2155)
				
					
				
			* Removed internal semconvgen * fix Makefile and update releasing docs Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com> Co-authored-by: Eddy Lin <elindy26@gmail.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							39acab3232
						
					
				
				
					commit
					23cb939611
				
			
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							| @@ -40,8 +40,8 @@ $(TOOLS)/%: | $(TOOLS) | ||||
| 	cd $(TOOLS_MOD_DIR) && \ | ||||
| 	$(GO) build -o $@ $(PACKAGE) | ||||
|  | ||||
| SEMCONVGEN = $(TOOLS)/semconv-gen | ||||
| $(TOOLS)/semconv-gen: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconv-gen | ||||
| SEMCONVGEN = $(TOOLS)/semconvgen | ||||
| $(TOOLS)/semconvgen: PACKAGE=go.opentelemetry.io/build-tools/semconvgen | ||||
|  | ||||
| CROSSLINK = $(TOOLS)/crosslink | ||||
| $(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/crosslink | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
| ## Semantic Convention Generation | ||||
|  | ||||
| If a new version of the OpenTelemetry Specification has been released it will be necessary to generate a new | ||||
| semantic convention package from the YAML definitions in the specification repository. There is a utility in | ||||
| `internal/tools/semconv-gen` that can be used to generate the a package with the name matching the specification | ||||
| semantic convention package from the YAML definitions in the specification repository. There is a `semconvgen` utility | ||||
| installed by `make tools` that can be used to generate the a package with the name matching the specification | ||||
| version number under the `semconv` package. This will ideally be done soon after the specification release is | ||||
| tagged. Make sure that the specification repo contains a checkout of the the latest tagged release so that the | ||||
| generated files match the released semantic conventions. | ||||
| @@ -12,9 +12,8 @@ generated files match the released semantic conventions. | ||||
| There are currently two categories of semantic conventions that must be generated, `resource` and `trace`. | ||||
|  | ||||
| ``` | ||||
| cd internal/tools/semconv-gen | ||||
| go run generator.go -i /path/to/specification/repo/semantic_conventions/resource | ||||
| go run generator.go -i /path/to/specification/repo/semantic_conventions/trace | ||||
| .tools/semconvgen -i /path/to/specification/repo/semantic_conventions/resource -t semconv/template.j2 | ||||
| .tools/semconvgen -i /path/to/specification/repo/semantic_conventions/trace -t semconv/template.j2 | ||||
| ``` | ||||
|  | ||||
| Using default values for all options other than `input` will result in using the `template.j2` template to | ||||
|   | ||||
| @@ -7,8 +7,7 @@ require ( | ||||
| 	github.com/gogo/protobuf v1.3.2 | ||||
| 	github.com/golangci/golangci-lint v1.41.1 | ||||
| 	github.com/itchyny/gojq v0.12.4 | ||||
| 	github.com/spf13/pflag v1.0.5 | ||||
| 	golang.org/x/mod v0.4.2 | ||||
| 	go.opentelemetry.io/build-tools/semconvgen v0.0.0-20210730171444-520d53fe242d | ||||
| 	golang.org/x/tools v0.1.5 | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -670,6 +670,10 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= | ||||
| go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opentelemetry.io/build-tools v0.0.0-20210719163622-92017e64f35b h1:tFMjUqEDGM2F82663yYidqTluwEJmmihk/AXr19J7rI= | ||||
| go.opentelemetry.io/build-tools v0.0.0-20210719163622-92017e64f35b/go.mod h1:zZRrJN8qdwDdPNkCEyww4SW54mM1Da0v9H3TyQet9T4= | ||||
| go.opentelemetry.io/build-tools/semconvgen v0.0.0-20210730171444-520d53fe242d h1:EjYSijh2wWPht3w0Zfa6V7280z5iDBr1228TysQgurM= | ||||
| go.opentelemetry.io/build-tools/semconvgen v0.0.0-20210730171444-520d53fe242d/go.mod h1:QJyAzHKDKGDl52AUIAZhWKx3nOPBZJ3dD44utso2FPE= | ||||
| go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | ||||
| go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | ||||
| go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= | ||||
|   | ||||
| @@ -1,407 +0,0 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
|  | ||||
| 	flag "github.com/spf13/pflag" | ||||
| 	"golang.org/x/mod/semver" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/internal/tools" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// Plain log output, no timestamps. | ||||
| 	log.SetFlags(0) | ||||
|  | ||||
| 	cfg := config{} | ||||
| 	flag.StringVarP(&cfg.inputPath, "input", "i", "", "Path to semantic convention definition YAML. Should be a directory in the specification git repository.") | ||||
| 	flag.StringVarP(&cfg.specVersion, "specver", "s", "", "Version of semantic convention to generate. Must be an existing version tag in the specification git repository.") | ||||
| 	flag.StringVarP(&cfg.outputPath, "output", "o", "", "Path to output target. Must be either an absolute path or relative to the repository root. If unspecified will output to a sub-directory with the name matching the version number specified via --specver flag.") | ||||
| 	flag.StringVarP(&cfg.containerImage, "container", "c", "otel/semconvgen", "Container image ID") | ||||
| 	flag.StringVarP(&cfg.outputFilename, "filename", "f", "", "Filename for templated output. If not specified 'basename(inputPath).go' will be used.") | ||||
| 	flag.StringVarP(&cfg.templateFilename, "template", "t", "template.j2", "Template filename") | ||||
| 	flag.Parse() | ||||
|  | ||||
| 	cfg, err := validateConfig(cfg) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		flag.Usage() | ||||
| 		os.Exit(-1) | ||||
| 	} | ||||
|  | ||||
| 	err = render(cfg) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	err = fixIdentifiers(cfg) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	err = format(cfg.outputFilename) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type config struct { | ||||
| 	inputPath        string | ||||
| 	outputPath       string | ||||
| 	outputFilename   string | ||||
| 	templateFilename string | ||||
| 	containerImage   string | ||||
| 	specVersion      string | ||||
| } | ||||
|  | ||||
| func validateConfig(cfg config) (config, error) { | ||||
| 	if cfg.inputPath == "" { | ||||
| 		return config{}, errors.New("input path must be provided") | ||||
| 	} | ||||
|  | ||||
| 	if cfg.outputFilename == "" { | ||||
| 		cfg.outputFilename = fmt.Sprintf("%s.go", path.Base(cfg.inputPath)) | ||||
| 	} | ||||
|  | ||||
| 	if cfg.specVersion == "" { | ||||
| 		// Find the latest version of the specification and use it for generation. | ||||
| 		var err error | ||||
| 		cfg.specVersion, err = findLatestSpecVersion(cfg) | ||||
| 		if err != nil { | ||||
| 			return config{}, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if cfg.outputPath == "" { | ||||
| 		// If output path is unspecified put it under a sub-directory with a name matching | ||||
| 		// the version of semantic convention under the semconv directory. | ||||
| 		cfg.outputPath = path.Join("semconv", cfg.specVersion) | ||||
| 	} | ||||
|  | ||||
| 	if !path.IsAbs(cfg.outputPath) { | ||||
| 		root, err := tools.FindRepoRoot() | ||||
| 		if err != nil { | ||||
| 			return config{}, err | ||||
| 		} | ||||
| 		cfg.outputPath = path.Join(root, cfg.outputPath) | ||||
| 	} | ||||
|  | ||||
| 	cfg.outputFilename = path.Join(cfg.outputPath, cfg.outputFilename) | ||||
|  | ||||
| 	if !path.IsAbs(cfg.templateFilename) { | ||||
| 		pwd, err := os.Getwd() | ||||
| 		if err != nil { | ||||
| 			return config{}, err | ||||
| 		} | ||||
| 		cfg.templateFilename = path.Join(pwd, cfg.templateFilename) | ||||
| 	} | ||||
|  | ||||
| 	return cfg, nil | ||||
| } | ||||
|  | ||||
| func render(cfg config) error { | ||||
| 	tmpDir, err := os.MkdirTemp("", "otel_semconvgen") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to create temporary directory: %w", err) | ||||
| 	} | ||||
| 	defer os.RemoveAll(tmpDir) | ||||
|  | ||||
| 	specCheckoutPath := path.Join(tmpDir, "input") | ||||
| 	err = os.Mkdir(specCheckoutPath, 0700) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to create input directory: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	outputPath := path.Join(tmpDir, "output") | ||||
| 	err = os.Mkdir(outputPath, 0700) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to create output directory: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// Checkout the specification repo to a temp dir. This will be the input | ||||
| 	// for the generator. | ||||
| 	doneFunc, err := checkoutSpecToDir(cfg, specCheckoutPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer doneFunc() | ||||
|  | ||||
| 	err = exec.Command("cp", cfg.templateFilename, tmpDir).Run() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to copy template to temp directory: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	cmd := exec.Command("docker", "run", "--rm", | ||||
| 		"-v", fmt.Sprintf("%s:/data", tmpDir), | ||||
| 		cfg.containerImage, | ||||
| 		"--yaml-root", path.Join("/data/input/semantic_conventions/", path.Base(cfg.inputPath)), | ||||
| 		"code", | ||||
| 		"--template", path.Join("/data", path.Base(cfg.templateFilename)), | ||||
| 		"--output", path.Join("/data/output", path.Base(cfg.outputFilename)), | ||||
| 	) | ||||
| 	err = cmd.Run() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to render template: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	err = os.MkdirAll(cfg.outputPath, 0700) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to create output directory %s: %w", cfg.outputPath, err) | ||||
| 	} | ||||
| 	err = exec.Command("cp", path.Join(tmpDir, "output", path.Base(cfg.outputFilename)), cfg.outputPath).Run() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to copy result to target: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type semVerSlice []string | ||||
|  | ||||
| func (s semVerSlice) Len() int { | ||||
| 	return len(s) | ||||
| } | ||||
|  | ||||
| func (s semVerSlice) Swap(i, j int) { | ||||
| 	s[i], s[j] = s[j], s[i] | ||||
| } | ||||
|  | ||||
| func (s semVerSlice) Less(i, j int) bool { | ||||
| 	return semver.Compare(s[i], s[j]) < 0 | ||||
| } | ||||
|  | ||||
| // findLatestSpecVersion finds the latest specification version number and checkouts | ||||
| // that version in the repo's working directory. | ||||
| func findLatestSpecVersion(cfg config) (string, error) { | ||||
| 	// List all tags in the specification repo. All released version numbers are tags | ||||
| 	// in the repo. | ||||
| 	cmd := exec.Command("git", "tag") | ||||
| 	// The specification repo is in cfg.inputPath. | ||||
| 	cmd.Dir = cfg.inputPath | ||||
| 	output, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("unable to exec %s: %w", cmd.String(), err) | ||||
| 	} | ||||
|  | ||||
| 	// Split the output: each line is a tag. | ||||
| 	lines := strings.Split(string(output), "\n") | ||||
|  | ||||
| 	// Copy valid semver version numbers to a slice. | ||||
| 	var versions semVerSlice | ||||
| 	for _, line := range lines { | ||||
| 		ver := line | ||||
| 		if semver.IsValid(ver) { | ||||
| 			versions = append(versions, ver) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Sort it according to semver rules. | ||||
| 	sort.Sort(versions) | ||||
|  | ||||
| 	if len(versions) == 0 { | ||||
| 		return "", fmt.Errorf("no version tags found in the specification repo at %s", cfg.inputPath) | ||||
| 	} | ||||
|  | ||||
| 	// Use the latest version number. | ||||
| 	lastVer := versions[len(versions)-1] | ||||
| 	return lastVer, nil | ||||
| } | ||||
|  | ||||
| // checkoutSpecToDir checks out the specification repository to the toDir. | ||||
| // Returned doneFunc should be called when the directory is no longer needed and can be | ||||
| // cleaned up. | ||||
| func checkoutSpecToDir(cfg config, toDir string) (doneFunc func(), err error) { | ||||
| 	// Checkout the selected tag to make sure we use the correct version of semantic | ||||
| 	// convention yaml files as the input. We will checkout the worktree to a temporary toDir. | ||||
| 	cmd := exec.Command("git", "worktree", "add", toDir, cfg.specVersion) | ||||
| 	// The specification repo is in cfg.inputPath. | ||||
| 	cmd.Dir = cfg.inputPath | ||||
| 	err = cmd.Run() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to exec %s: %w", cmd.String(), err) | ||||
| 	} | ||||
|  | ||||
| 	doneFunc = func() { | ||||
| 		// Remove the worktree when it is no longer needed. | ||||
| 		cmd := exec.Command("git", "worktree", "remove", "-f", toDir) | ||||
| 		cmd.Dir = cfg.inputPath | ||||
| 		err := cmd.Run() | ||||
| 		if err != nil { | ||||
| 			log.Printf("Could not cleanup spec repo worktree, unable to exec %s: %s\n", cmd.String(), err.Error()) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return doneFunc, nil | ||||
| } | ||||
|  | ||||
| var capitalizations = []string{ | ||||
| 	"ACL", | ||||
| 	"AIX", | ||||
| 	"AKS", | ||||
| 	"AMD64", | ||||
| 	"API", | ||||
| 	"ARM32", | ||||
| 	"ARM64", | ||||
| 	"ARN", | ||||
| 	"ARNs", | ||||
| 	"ASCII", | ||||
| 	"AWS", | ||||
| 	"CPP", | ||||
| 	"CPU", | ||||
| 	"CSS", | ||||
| 	"DB", | ||||
| 	"DC", | ||||
| 	"DNS", | ||||
| 	"EC2", | ||||
| 	"ECS", | ||||
| 	"EDB", | ||||
| 	"EKS", | ||||
| 	"EOF", | ||||
| 	"GCP", | ||||
| 	"GRPC", | ||||
| 	"GUID", | ||||
| 	"HPUX", | ||||
| 	"HSQLDB", | ||||
| 	"HTML", | ||||
| 	"HTTP", | ||||
| 	"HTTPS", | ||||
| 	"IA64", | ||||
| 	"ID", | ||||
| 	"IP", | ||||
| 	"JDBC", | ||||
| 	"JSON", | ||||
| 	"K8S", | ||||
| 	"LHS", | ||||
| 	"MSSQL", | ||||
| 	"OS", | ||||
| 	"PHP", | ||||
| 	"PID", | ||||
| 	"PPC32", | ||||
| 	"PPC64", | ||||
| 	"QPS", | ||||
| 	"QUIC", | ||||
| 	"RAM", | ||||
| 	"RHS", | ||||
| 	"RPC", | ||||
| 	"SDK", | ||||
| 	"SLA", | ||||
| 	"SMTP", | ||||
| 	"SPDY", | ||||
| 	"SQL", | ||||
| 	"SSH", | ||||
| 	"TCP", | ||||
| 	"TLS", | ||||
| 	"TTL", | ||||
| 	"UDP", | ||||
| 	"UID", | ||||
| 	"UI", | ||||
| 	"UUID", | ||||
| 	"URI", | ||||
| 	"URL", | ||||
| 	"UTF8", | ||||
| 	"VM", | ||||
| 	"XML", | ||||
| 	"XMPP", | ||||
| 	"XSRF", | ||||
| 	"XSS", | ||||
| 	"ZOS", | ||||
| 	"CronJob", | ||||
| 	"WebEngine", | ||||
| 	"MySQL", | ||||
| 	"PostgreSQL", | ||||
| 	"MariaDB", | ||||
| 	"MaxDB", | ||||
| 	"FirstSQL", | ||||
| 	"InstantDB", | ||||
| 	"HBase", | ||||
| 	"MongoDB", | ||||
| 	"CouchDB", | ||||
| 	"CosmosDB", | ||||
| 	"DynamoDB", | ||||
| 	"HanaDB", | ||||
| 	"FreeBSD", | ||||
| 	"NetBSD", | ||||
| 	"OpenBSD", | ||||
| 	"DragonflyBSD", | ||||
| 	"InProc", | ||||
| 	"FaaS", | ||||
| } | ||||
|  | ||||
| // These are not simple capitalization fixes, but require string replacement. | ||||
| // All occurrences of the key will be replaced with the corresponding value. | ||||
| var replacements = map[string]string{ | ||||
| 	"RedisDatabase": "RedisDB", | ||||
| 	"IPTCP":         "TCP", | ||||
| 	"IPUDP":         "UDP", | ||||
| 	"Lineno":        "LineNumber", | ||||
| } | ||||
|  | ||||
| func fixIdentifiers(cfg config) error { | ||||
| 	data, err := ioutil.ReadFile(cfg.outputFilename) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to read file: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	for _, init := range capitalizations { | ||||
| 		// Match the title-cased capitalization target, asserting that its followed by | ||||
| 		// either a capital letter, whitespace, a digit, or the end of text. | ||||
| 		// This is to avoid, e.g., turning "Identifier" into "IDentifier". | ||||
| 		re := regexp.MustCompile(strings.Title(strings.ToLower(init)) + `([A-Z\s\d]|\b|$)`) | ||||
| 		// RE2 does not support zero-width lookahead assertions, so we have to replace | ||||
| 		// the last character that may have matched the first capture group in the | ||||
| 		// expression constructed above. | ||||
| 		data = re.ReplaceAll(data, []byte(init+`$1`)) | ||||
| 	} | ||||
|  | ||||
| 	for cur, repl := range replacements { | ||||
| 		data = bytes.ReplaceAll(data, []byte(cur), []byte(repl)) | ||||
| 	} | ||||
|  | ||||
| 	// Inject the correct import path. | ||||
| 	packageDir := path.Base(path.Dir(cfg.outputFilename)) | ||||
| 	importPath := fmt.Sprintf(`"go.opentelemetry.io/otel/semconv/%s"`, packageDir) | ||||
| 	data = bytes.ReplaceAll(data, []byte(`[[IMPORTPATH]]`), []byte(importPath)) | ||||
|  | ||||
| 	err = ioutil.WriteFile(cfg.outputFilename, data, 0644) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to write updated file: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func format(fn string) error { | ||||
| 	cmd := exec.Command("gofmt", "-w", "-s", fn) | ||||
| 	cmd.Stdout = os.Stdout | ||||
| 	cmd.Stderr = os.Stderr | ||||
| 	err := cmd.Run() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to format updated file: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -21,5 +21,6 @@ import ( | ||||
| 	_ "github.com/gogo/protobuf/protoc-gen-gogofast" | ||||
| 	_ "github.com/golangci/golangci-lint/cmd/golangci-lint" | ||||
| 	_ "github.com/itchyny/gojq" | ||||
| 	_ "go.opentelemetry.io/build-tools/semconvgen" | ||||
| 	_ "golang.org/x/tools/cmd/stringer" | ||||
| ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user