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:
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"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user