Add telemetry reporting through checkpoint
Will report builders/provisioner/post-processor types used per build, and whether or not the build passed. Will also report any panics we see. You may opt out of this reporting by setting the environment variable `CHECKPOINT_DISABLE`.
This commit is contained in:
parent
3a579bea81
commit
7382382727
21
main.go
21
main.go
|
@ -73,6 +73,14 @@ func realMain() int {
|
|||
outR, outW := io.Pipe()
|
||||
go copyOutput(outR, doneCh)
|
||||
|
||||
// Enable checkpoint for panic reporting
|
||||
config, err := loadConfig()
|
||||
if err == nil {
|
||||
if !config.DisableCheckpoint {
|
||||
packer.CheckpointReporter.Enable(config.DisableCheckpointSignature)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the configuration for panicwrap and wrap our executable
|
||||
wrapConfig.Handler = panicHandler(logTempFile)
|
||||
wrapConfig.Writer = io.MultiWriter(logTempFile, logWriter)
|
||||
|
@ -117,9 +125,11 @@ func wrappedMain() int {
|
|||
log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH)
|
||||
log.Printf("Built with Go Version: %s", runtime.Version())
|
||||
|
||||
inPlugin := os.Getenv(plugin.MagicCookieKey) == plugin.MagicCookieValue
|
||||
|
||||
// Prepare stdin for plugin usage by switching it to a pipe
|
||||
// But do not switch to pipe in plugin
|
||||
if os.Getenv(plugin.MagicCookieKey) != plugin.MagicCookieValue {
|
||||
if !inPlugin {
|
||||
setupStdin()
|
||||
}
|
||||
|
||||
|
@ -132,6 +142,9 @@ func wrappedMain() int {
|
|||
|
||||
// Fire off the checkpoint.
|
||||
go runCheckpoint(config)
|
||||
if !config.DisableCheckpoint {
|
||||
packer.CheckpointReporter.Enable(config.DisableCheckpointSignature)
|
||||
}
|
||||
|
||||
cacheDir := os.Getenv("PACKER_CACHE_DIR")
|
||||
if cacheDir == "" {
|
||||
|
@ -196,6 +209,12 @@ func wrappedMain() int {
|
|||
}
|
||||
|
||||
exitCode, err := cli.Run()
|
||||
if !inPlugin {
|
||||
if err := packer.CheckpointReporter.Finalize(cli.Subcommand(), exitCode, err); err != nil {
|
||||
log.Printf("Error finalizing telemetry report. This is safe to ignore. %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err)
|
||||
return 1
|
||||
|
|
|
@ -115,6 +115,7 @@ type coreBuildPostProcessor struct {
|
|||
// Keeps track of the provisioner and the configuration of the provisioner
|
||||
// within the build.
|
||||
type coreBuildProvisioner struct {
|
||||
pType string
|
||||
provisioner Provisioner
|
||||
config []interface{}
|
||||
}
|
||||
|
@ -193,17 +194,13 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) {
|
|||
|
||||
// Add a hook for the provisioners if we have provisioners
|
||||
if len(b.provisioners) > 0 {
|
||||
provisioners := make([]Provisioner, len(b.provisioners))
|
||||
for i, p := range b.provisioners {
|
||||
provisioners[i] = p.provisioner
|
||||
}
|
||||
|
||||
if _, ok := hooks[HookProvision]; !ok {
|
||||
hooks[HookProvision] = make([]Hook, 0, 1)
|
||||
}
|
||||
|
||||
hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{
|
||||
Provisioners: provisioners,
|
||||
Provisioners: b.provisioners,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -217,7 +214,9 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) {
|
|||
}
|
||||
|
||||
log.Printf("Running builder: %s", b.builderType)
|
||||
ts := CheckpointReporter.AddSpan(b.builderType, "builder")
|
||||
builderArtifact, err := b.builder.Run(builderUi, hook, cache)
|
||||
ts.End(err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -242,7 +241,9 @@ PostProcessorRunSeqLoop:
|
|||
}
|
||||
|
||||
builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.processorType))
|
||||
ts := CheckpointReporter.AddSpan(corePP.processorType, "post-processor")
|
||||
artifact, keep, err := corePP.processor.PostProcess(ppUi, priorArtifact)
|
||||
ts.End(err)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("Post-processor failed: %s", err))
|
||||
continue PostProcessorRunSeqLoop
|
||||
|
|
|
@ -154,6 +154,7 @@ func (c *Core) Build(n string) (Build, error) {
|
|||
}
|
||||
|
||||
provisioners = append(provisioners, coreBuildProvisioner{
|
||||
pType: rawP.Type,
|
||||
provisioner: provisioner,
|
||||
config: config,
|
||||
})
|
||||
|
|
|
@ -30,7 +30,7 @@ type Provisioner interface {
|
|||
type ProvisionHook struct {
|
||||
// The provisioners to run as part of the hook. These should already
|
||||
// be prepared (by calling Prepare) at some earlier stage.
|
||||
Provisioners []Provisioner
|
||||
Provisioners []coreBuildProvisioner
|
||||
|
||||
lock sync.Mutex
|
||||
runningProvisioner Provisioner
|
||||
|
@ -59,10 +59,13 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac
|
|||
|
||||
for _, p := range h.Provisioners {
|
||||
h.lock.Lock()
|
||||
h.runningProvisioner = p
|
||||
h.runningProvisioner = p.provisioner
|
||||
h.lock.Unlock()
|
||||
|
||||
if err := p.Provision(ui, comm); err != nil {
|
||||
ts := CheckpointReporter.AddSpan(p.pType, "provisioner")
|
||||
err := p.provisioner.Provision(ui, comm)
|
||||
ts.End(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package packer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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 += fmt.Sprintf("-%s", 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) log(m string, args ...interface{}) {
|
||||
}
|
||||
|
||||
func (c *CheckpointTelemetry) ReportPanic(m string) error {
|
||||
if !c.enabled {
|
||||
return nil
|
||||
}
|
||||
log.Printf("[TELEMETRY] Add panic: %s", m)
|
||||
panicParams := c.baseParams(TelemetryPanicVersion)
|
||||
panicParams.Payload = m
|
||||
panicParams.EndTime = time.Now().UTC()
|
||||
return checkpoint.Report(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
|
||||
}
|
||||
|
||||
log.Printf("[TELEMETRY] finalize: %#v", c)
|
||||
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
|
||||
|
||||
return checkpoint.Report(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())
|
||||
}
|
||||
}
|
5
panic.go
5
panic.go
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/mitchellh/panicwrap"
|
||||
)
|
||||
|
||||
|
@ -34,6 +35,10 @@ func panicHandler(logF *os.File) panicwrap.HandlerFunc {
|
|||
// shown in case anything below fails.
|
||||
fmt.Fprintf(os.Stderr, fmt.Sprintf("%s\n", m))
|
||||
|
||||
if err := packer.CheckpointReporter.ReportPanic(m); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to report panic. This is safe to ignore: %s", err)
|
||||
}
|
||||
|
||||
// Create the crash log file where we'll write the logs
|
||||
f, err := os.Create("crash.log")
|
||||
if err != nil {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
91
vendor/github.com/aws/aws-sdk-go/private/endpoints/endpoints_map.go
generated
vendored
Normal file
91
vendor/github.com/aws/aws-sdk-go/private/endpoints/endpoints_map.go
generated
vendored
Normal file
|
@ -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",
|
||||
},
|
||||
},
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
# Go Checkpoint Client
|
||||
|
||||
[Checkpoint](http://checkpoint.hashicorp.com) is an internal service at
|
||||
Hashicorp that we use to check version information, broadcoast security
|
||||
Hashicorp that we use to check version information, broadcast security
|
||||
bulletins, etc.
|
||||
|
||||
We understand that software making remote calls over the internet
|
||||
|
@ -10,7 +10,7 @@ disabled in all of our software that includes it. You can view the source
|
|||
of this client to see that we're not sending any private information.
|
||||
|
||||
Each Hashicorp application has it's specific configuration option
|
||||
to disable chekpoint calls, but the `CHECKPOINT_DISABLE` makes
|
||||
to disable checkpoint calls, but the `CHECKPOINT_DISABLE` makes
|
||||
the underlying checkpoint component itself disabled. For example
|
||||
in the case of packer:
|
||||
```
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package checkpoint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
@ -20,10 +21,113 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
)
|
||||
|
||||
var magicBytes [4]byte = [4]byte{0x35, 0x77, 0x69, 0xFB}
|
||||
|
||||
type ReportParams struct {
|
||||
// Signature is some random signature that should be stored and used
|
||||
// as a cookie-like value. This ensures that alerts aren't repeated.
|
||||
// If the signature is changed, repeat alerts may be sent down. The
|
||||
// signature should NOT be anything identifiable to a user (such as
|
||||
// a MAC address). It should be random.
|
||||
//
|
||||
// If SignatureFile is given, then the signature will be read from this
|
||||
// file. If the file doesn't exist, then a random signature will
|
||||
// automatically be generated and stored here. SignatureFile will be
|
||||
// ignored if Signature is given.
|
||||
Signature string `json:"signature"`
|
||||
SignatureFile string `json:"-"`
|
||||
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Version string `json:"version"`
|
||||
Product string `json:"product"`
|
||||
Payload interface{} `json:"payload,omitempty"`
|
||||
RunID string `json:"run_id"`
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
Args []string `json:"args"`
|
||||
SchemaVersion string `json:"schema_version"`
|
||||
}
|
||||
|
||||
func (i *ReportParams) signature() string {
|
||||
signature := i.Signature
|
||||
if i.Signature == "" && i.SignatureFile != "" {
|
||||
var err error
|
||||
signature, err = checkSignature(i.SignatureFile)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return signature
|
||||
}
|
||||
|
||||
func Report(r *ReportParams) error {
|
||||
if disabled := os.Getenv("CHECKPOINT_DISABLE"); disabled != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.RunID == "" {
|
||||
uuid, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.RunID = uuid
|
||||
}
|
||||
if r.Arch == "" {
|
||||
r.Arch = runtime.GOARCH
|
||||
}
|
||||
if r.OS == "" {
|
||||
r.OS = runtime.GOOS
|
||||
}
|
||||
if len(r.Args) == 0 {
|
||||
r.Args = os.Args
|
||||
}
|
||||
if r.Signature == "" {
|
||||
r.Signature = r.signature()
|
||||
}
|
||||
|
||||
b, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// file logging while debugging
|
||||
file, err := os.OpenFile("telemetry.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
file.Write(b)
|
||||
file.WriteString("\n")
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "checkpoint-api.hashicorp.com",
|
||||
Path: fmt.Sprintf("/v1/telemetry/%s", r.Product),
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", u.String(), bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("User-Agent", "HashiCorp/go-checkpoint")
|
||||
|
||||
client := cleanhttp.DefaultClient()
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 201 {
|
||||
return fmt.Errorf("Unknown status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckParams are the parameters for configuring a check request.
|
||||
type CheckParams struct {
|
||||
// Product and version are used to lookup the correct product and
|
||||
|
@ -116,6 +220,7 @@ func Check(p *CheckParams) (*CheckResponse, error) {
|
|||
p.OS = runtime.GOOS
|
||||
}
|
||||
|
||||
// TODO: race here if we try to write this file twice
|
||||
// If we're given a SignatureFile, then attempt to read that.
|
||||
signature := p.Signature
|
||||
if p.Signature == "" && p.SignatureFile != "" {
|
||||
|
|
|
@ -546,9 +546,10 @@
|
|||
"revision": "7554cd9344cec97297fa6649b055a8c98c2a1e55"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "nd3S1qkFv7zZxA9be0bw4nT0pe0=",
|
||||
"checksumSHA1": "rPdTLJsefFK1hgWuVXcM9wmnSMI=",
|
||||
"path": "github.com/hashicorp/go-checkpoint",
|
||||
"revision": "e4b2dc34c0f698ee04750bf2035d8b9384233e1b"
|
||||
"revision": "194925eac2c1f69fcac1693d3f02f1337c341763",
|
||||
"revisionTime": "2017-06-15T06:56:40Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "fSe5y1UgTDeYlnFfUcDA1zzcw+U=",
|
||||
|
|
Loading…
Reference in New Issue