mirror of https://github.com/hashicorp/packer
Merge pull request #5015 from hashicorp/telemetry
Add telemetry reporting through checkpointpull/5030/head
commit
31f2e31949
@ -0,0 +1,135 @@
|
||||
package packer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
checkpoint "github.com/hashicorp/go-checkpoint"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
const TelemetryVersion string = "beta/packer/4"
|
||||
const TelemetryPanicVersion string = "beta/packer_panic/4"
|
||||
|
||||
var CheckpointReporter CheckpointTelemetry
|
||||
|
||||
func init() {
|
||||
CheckpointReporter.startTime = time.Now().UTC()
|
||||
}
|
||||
|
||||
type PackerReport struct {
|
||||
Spans []*TelemetrySpan `json:"spans"`
|
||||
ExitCode int `json:"exit_code"`
|
||||
Error string `json:"error"`
|
||||
Command string `json:"command"`
|
||||
}
|
||||
|
||||
type CheckpointTelemetry struct {
|
||||
enabled bool
|
||||
spans []*TelemetrySpan
|
||||
signatureFile string
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
func (c *CheckpointTelemetry) Enable(disableSignature bool) {
|
||||
configDir, err := ConfigDir()
|
||||
if err != nil {
|
||||
log.Printf("[ERR] Checkpoint telemetry setup error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
signatureFile := ""
|
||||
if disableSignature {
|
||||
log.Printf("[INFO] Checkpoint telemetry signature disabled")
|
||||
} else {
|
||||
signatureFile = filepath.Join(configDir, "checkpoint_signature")
|
||||
}
|
||||
|
||||
c.signatureFile = signatureFile
|
||||
c.enabled = true
|
||||
}
|
||||
|
||||
func (c *CheckpointTelemetry) baseParams(prefix string) *checkpoint.ReportParams {
|
||||
version := packerVersion.Version
|
||||
if packerVersion.VersionPrerelease != "" {
|
||||
version += "-" + packerVersion.VersionPrerelease
|
||||
}
|
||||
|
||||
return &checkpoint.ReportParams{
|
||||
Product: "packer",
|
||||
SchemaVersion: prefix,
|
||||
StartTime: c.startTime,
|
||||
Version: version,
|
||||
RunID: os.Getenv("PACKER_RUN_UUID"),
|
||||
SignatureFile: c.signatureFile,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CheckpointTelemetry) ReportPanic(m string) error {
|
||||
if !c.enabled {
|
||||
return nil
|
||||
}
|
||||
panicParams := c.baseParams(TelemetryPanicVersion)
|
||||
panicParams.Payload = m
|
||||
panicParams.EndTime = time.Now().UTC()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 4500*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
return checkpoint.Report(ctx, panicParams)
|
||||
}
|
||||
|
||||
func (c *CheckpointTelemetry) AddSpan(name, pluginType string) *TelemetrySpan {
|
||||
log.Printf("[TELEMETRY] Starting %s %s", pluginType, name)
|
||||
ts := &TelemetrySpan{
|
||||
Name: name,
|
||||
Type: pluginType,
|
||||
StartTime: time.Now().UTC(),
|
||||
}
|
||||
c.spans = append(c.spans, ts)
|
||||
return ts
|
||||
}
|
||||
|
||||
func (c *CheckpointTelemetry) Finalize(command string, errCode int, err error) error {
|
||||
if !c.enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
params := c.baseParams(TelemetryVersion)
|
||||
params.EndTime = time.Now().UTC()
|
||||
|
||||
extra := &PackerReport{
|
||||
Spans: c.spans,
|
||||
ExitCode: errCode,
|
||||
Command: command,
|
||||
}
|
||||
if err != nil {
|
||||
extra.Error = err.Error()
|
||||
}
|
||||
params.Payload = extra
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 450*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
return checkpoint.Report(ctx, params)
|
||||
}
|
||||
|
||||
type TelemetrySpan struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func (s *TelemetrySpan) End(err error) {
|
||||
s.EndTime = time.Now().UTC()
|
||||
log.Printf("[TELEMETRY] ending %s", s.Name)
|
||||
if err != nil {
|
||||
s.Error = err.Error()
|
||||
log.Printf("[TELEMETRY] ERROR: %s", err.Error())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
// Package endpoints validates regional endpoints for services.
|
||||
package endpoints
|
||||
|
||||
//go:generate go run ../model/cli/gen-endpoints/main.go endpoints.json endpoints_map.go
|
||||
//go:generate gofmt -s -w endpoints_map.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NormalizeEndpoint takes and endpoint and service API information to return a
|
||||
// normalized endpoint and signing region. If the endpoint is not an empty string
|
||||
// the service name and region will be used to look up the service's API endpoint.
|
||||
// If the endpoint is provided the scheme will be added if it is not present.
|
||||
func NormalizeEndpoint(endpoint, serviceName, region string, disableSSL, useDualStack bool) (normEndpoint, signingRegion string) {
|
||||
if endpoint == "" {
|
||||
return EndpointForRegion(serviceName, region, disableSSL, useDualStack)
|
||||
}
|
||||
|
||||
return AddScheme(endpoint, disableSSL), ""
|
||||
}
|
||||
|
||||
// EndpointForRegion returns an endpoint and its signing region for a service and region.
|
||||
// if the service and region pair are not found endpoint and signingRegion will be empty.
|
||||
func EndpointForRegion(svcName, region string, disableSSL, useDualStack bool) (endpoint, signingRegion string) {
|
||||
dualStackField := ""
|
||||
if useDualStack {
|
||||
dualStackField = "/dualstack"
|
||||
}
|
||||
|
||||
derivedKeys := []string{
|
||||
region + "/" + svcName + dualStackField,
|
||||
region + "/*" + dualStackField,
|
||||
"*/" + svcName + dualStackField,
|
||||
"*/*" + dualStackField,
|
||||
}
|
||||
|
||||
for _, key := range derivedKeys {
|
||||
if val, ok := endpointsMap.Endpoints[key]; ok {
|
||||
ep := val.Endpoint
|
||||
ep = strings.Replace(ep, "{region}", region, -1)
|
||||
ep = strings.Replace(ep, "{service}", svcName, -1)
|
||||
|
||||
endpoint = ep
|
||||
signingRegion = val.SigningRegion
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return AddScheme(endpoint, disableSSL), signingRegion
|
||||
}
|
||||
|
||||
// Regular expression to determine if the endpoint string is prefixed with a scheme.
|
||||
var schemeRE = regexp.MustCompile("^([^:]+)://")
|
||||
|
||||
// AddScheme adds the HTTP or HTTPS schemes to a endpoint URL if there is no
|
||||
// scheme. If disableSSL is true HTTP will be added instead of the default HTTPS.
|
||||
func AddScheme(endpoint string, disableSSL bool) string {
|
||||
if endpoint != "" && !schemeRE.MatchString(endpoint) {
|
||||
scheme := "https"
|
||||
if disableSSL {
|
||||
scheme = "http"
|
||||
}
|
||||
endpoint = fmt.Sprintf("%s://%s", scheme, endpoint)
|
||||
}
|
||||
|
||||
return endpoint
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
{
|
||||
"version": 2,
|
||||
"endpoints": {
|
||||
"*/*": {
|
||||
"endpoint": "{service}.{region}.amazonaws.com"
|
||||
},
|
||||
"cn-north-1/*": {
|
||||
"endpoint": "{service}.{region}.amazonaws.com.cn",
|
||||
"signatureVersion": "v4"
|
||||
},
|
||||
"cn-north-1/ec2metadata": {
|
||||
"endpoint": "http://169.254.169.254/latest"
|
||||
},
|
||||
"us-gov-west-1/iam": {
|
||||
"endpoint": "iam.us-gov.amazonaws.com"
|
||||
},
|
||||
"us-gov-west-1/sts": {
|
||||
"endpoint": "sts.us-gov-west-1.amazonaws.com"
|
||||
},
|
||||
"us-gov-west-1/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"us-gov-west-1/ec2metadata": {
|
||||
"endpoint": "http://169.254.169.254/latest"
|
||||
},
|
||||
"*/cloudfront": {
|
||||
"endpoint": "cloudfront.amazonaws.com",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"*/cloudsearchdomain": {
|
||||
"endpoint": "",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"*/data.iot": {
|
||||
"endpoint": "",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"*/ec2metadata": {
|
||||
"endpoint": "http://169.254.169.254/latest"
|
||||
},
|
||||
"*/iam": {
|
||||
"endpoint": "iam.amazonaws.com",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"*/importexport": {
|
||||
"endpoint": "importexport.amazonaws.com",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"*/route53": {
|
||||
"endpoint": "route53.amazonaws.com",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"*/sts": {
|
||||
"endpoint": "sts.amazonaws.com",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"*/waf": {
|
||||
"endpoint": "waf.amazonaws.com",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"us-east-1/sdb": {
|
||||
"endpoint": "sdb.amazonaws.com",
|
||||
"signingRegion": "us-east-1"
|
||||
},
|
||||
"*/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"*/s3/dualstack": {
|
||||
"endpoint": "s3.dualstack.{region}.amazonaws.com"
|
||||
},
|
||||
"us-east-1/s3": {
|
||||
"endpoint": "s3.amazonaws.com"
|
||||
},
|
||||
"eu-central-1/s3": {
|
||||
"endpoint": "{service}.{region}.amazonaws.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package endpoints
|
||||
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
|
||||
type endpointStruct struct {
|
||||
Version int
|
||||
Endpoints map[string]endpointEntry
|
||||
}
|
||||
|
||||
type endpointEntry struct {
|
||||
Endpoint string
|
||||
SigningRegion string
|
||||
}
|
||||
|
||||
var endpointsMap = endpointStruct{
|
||||
Version: 2,
|
||||
Endpoints: map[string]endpointEntry{
|
||||
"*/*": {
|
||||
Endpoint: "{service}.{region}.amazonaws.com",
|
||||
},
|
||||
"*/cloudfront": {
|
||||
Endpoint: "cloudfront.amazonaws.com",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"*/cloudsearchdomain": {
|
||||
Endpoint: "",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"*/data.iot": {
|
||||
Endpoint: "",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"*/ec2metadata": {
|
||||
Endpoint: "http://169.254.169.254/latest",
|
||||
},
|
||||
"*/iam": {
|
||||
Endpoint: "iam.amazonaws.com",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"*/importexport": {
|
||||
Endpoint: "importexport.amazonaws.com",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"*/route53": {
|
||||
Endpoint: "route53.amazonaws.com",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"*/s3": {
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"*/s3/dualstack": {
|
||||
Endpoint: "s3.dualstack.{region}.amazonaws.com",
|
||||
},
|
||||
"*/sts": {
|
||||
Endpoint: "sts.amazonaws.com",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"*/waf": {
|
||||
Endpoint: "waf.amazonaws.com",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"cn-north-1/*": {
|
||||
Endpoint: "{service}.{region}.amazonaws.com.cn",
|
||||
},
|
||||
"cn-north-1/ec2metadata": {
|
||||
Endpoint: "http://169.254.169.254/latest",
|
||||
},
|
||||
"eu-central-1/s3": {
|
||||
Endpoint: "{service}.{region}.amazonaws.com",
|
||||
},
|
||||
"us-east-1/s3": {
|
||||
Endpoint: "s3.amazonaws.com",
|
||||
},
|
||||
"us-east-1/sdb": {
|
||||
Endpoint: "sdb.amazonaws.com",
|
||||
SigningRegion: "us-east-1",
|
||||
},
|
||||
"us-gov-west-1/ec2metadata": {
|
||||
Endpoint: "http://169.254.169.254/latest",
|
||||
},
|
||||
"us-gov-west-1/iam": {
|
||||
Endpoint: "iam.us-gov.amazonaws.com",
|
||||
},
|
||||
"us-gov-west-1/s3": {
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"us-gov-west-1/sts": {
|
||||
Endpoint: "sts.us-gov-west-1.amazonaws.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
Loading…
Reference in new issue