1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2024-12-24 20:14: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:
Anthony Mirabella 2021-08-02 14:39:18 -04:00 committed by GitHub
parent 39acab3232
commit 23cb939611
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 12 additions and 416 deletions

View File

@ -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

View File

@ -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

View File

@ -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
)

View File

@ -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=

View File

@ -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
}

View File

@ -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"
)