Browse Source

users listing

master
Vladimir Smagin 4 months ago
parent
commit
78b5e35957
100 changed files with 18153 additions and 30 deletions
  1. 249
    0
      Gopkg.lock
  2. 6
    0
      Gopkg.toml
  3. 23
    28
      cmd/import.go
  4. 53
    0
      cmd/list.go
  5. 26
    2
      cmd/root.go
  6. 27
    0
      keys/johnny_gomez.pem
  7. 1
    0
      keys/johnny_gomez.pem.pub
  8. 27
    0
      keys/nick_diamond.pem
  9. 1
    0
      keys/nick_diamond.pem.pub
  10. 27
    0
      keys/stacy_combred.pem
  11. 1
    0
      keys/stacy_combred.pem.pub
  12. 3
    0
      vendor/github.com/certifi/gocertifi/LICENSE
  13. 60
    0
      vendor/github.com/certifi/gocertifi/README.md
  14. 4456
    0
      vendor/github.com/certifi/gocertifi/certifi.go
  15. 10
    0
      vendor/github.com/certifi/gocertifi/certifi_test.go
  16. 20
    0
      vendor/github.com/certifi/gocertifi/tasks.py
  17. 1
    0
      vendor/github.com/getsentry/raven-go/.dockerignore
  18. 5
    0
      vendor/github.com/getsentry/raven-go/.gitignore
  19. 0
    0
      vendor/github.com/getsentry/raven-go/.gitmodules
  20. 41
    0
      vendor/github.com/getsentry/raven-go/.travis.yml
  21. 28
    0
      vendor/github.com/getsentry/raven-go/LICENSE
  22. 19
    0
      vendor/github.com/getsentry/raven-go/README.md
  23. 977
    0
      vendor/github.com/getsentry/raven-go/client.go
  24. 319
    0
      vendor/github.com/getsentry/raven-go/client_test.go
  25. 60
    0
      vendor/github.com/getsentry/raven-go/errors.go
  26. 144
    0
      vendor/github.com/getsentry/raven-go/errors_test.go
  27. 42
    0
      vendor/github.com/getsentry/raven-go/example/example.go
  28. 28
    0
      vendor/github.com/getsentry/raven-go/examples_test.go
  29. 50
    0
      vendor/github.com/getsentry/raven-go/exception.go
  30. 39
    0
      vendor/github.com/getsentry/raven-go/exception_test.go
  31. 99
    0
      vendor/github.com/getsentry/raven-go/http.go
  32. 150
    0
      vendor/github.com/getsentry/raven-go/http_test.go
  33. 49
    0
      vendor/github.com/getsentry/raven-go/interfaces.go
  34. 4
    0
      vendor/github.com/getsentry/raven-go/runtests.sh
  35. 277
    0
      vendor/github.com/getsentry/raven-go/stacktrace.go
  36. 189
    0
      vendor/github.com/getsentry/raven-go/stacktrace_test.go
  37. 20
    0
      vendor/github.com/getsentry/raven-go/writer.go
  38. 16
    0
      vendor/github.com/golang/snappy/.gitignore
  39. 15
    0
      vendor/github.com/golang/snappy/AUTHORS
  40. 37
    0
      vendor/github.com/golang/snappy/CONTRIBUTORS
  41. 27
    0
      vendor/github.com/golang/snappy/LICENSE
  42. 107
    0
      vendor/github.com/golang/snappy/README
  43. 46
    0
      vendor/github.com/golang/snappy/cmd/snappytool/main.go
  44. 237
    0
      vendor/github.com/golang/snappy/decode.go
  45. 14
    0
      vendor/github.com/golang/snappy/decode_amd64.go
  46. 490
    0
      vendor/github.com/golang/snappy/decode_amd64.s
  47. 101
    0
      vendor/github.com/golang/snappy/decode_other.go
  48. 285
    0
      vendor/github.com/golang/snappy/encode.go
  49. 29
    0
      vendor/github.com/golang/snappy/encode_amd64.go
  50. 730
    0
      vendor/github.com/golang/snappy/encode_amd64.s
  51. 238
    0
      vendor/github.com/golang/snappy/encode_other.go
  52. 1
    0
      vendor/github.com/golang/snappy/go.mod
  53. 1965
    0
      vendor/github.com/golang/snappy/golden_test.go
  54. 79
    0
      vendor/github.com/golang/snappy/misc/main.cpp
  55. 98
    0
      vendor/github.com/golang/snappy/snappy.go
  56. 1353
    0
      vendor/github.com/golang/snappy/snappy_test.go
  57. 396
    0
      vendor/github.com/golang/snappy/testdata/Mark.Twain-Tom.Sawyer.txt
  58. BIN
      vendor/github.com/golang/snappy/testdata/Mark.Twain-Tom.Sawyer.txt.rawsnappy
  59. 354
    0
      vendor/github.com/hashicorp/errwrap/LICENSE
  60. 89
    0
      vendor/github.com/hashicorp/errwrap/README.md
  61. 169
    0
      vendor/github.com/hashicorp/errwrap/errwrap.go
  62. 94
    0
      vendor/github.com/hashicorp/errwrap/errwrap_test.go
  63. 1
    0
      vendor/github.com/hashicorp/errwrap/go.mod
  64. 363
    0
      vendor/github.com/hashicorp/go-cleanhttp/LICENSE
  65. 30
    0
      vendor/github.com/hashicorp/go-cleanhttp/README.md
  66. 57
    0
      vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go
  67. 20
    0
      vendor/github.com/hashicorp/go-cleanhttp/doc.go
  68. 1
    0
      vendor/github.com/hashicorp/go-cleanhttp/go.mod
  69. 48
    0
      vendor/github.com/hashicorp/go-cleanhttp/handlers.go
  70. 72
    0
      vendor/github.com/hashicorp/go-cleanhttp/handlers_test.go
  71. 12
    0
      vendor/github.com/hashicorp/go-multierror/.travis.yml
  72. 353
    0
      vendor/github.com/hashicorp/go-multierror/LICENSE
  73. 31
    0
      vendor/github.com/hashicorp/go-multierror/Makefile
  74. 97
    0
      vendor/github.com/hashicorp/go-multierror/README.md
  75. 41
    0
      vendor/github.com/hashicorp/go-multierror/append.go
  76. 82
    0
      vendor/github.com/hashicorp/go-multierror/append_test.go
  77. 26
    0
      vendor/github.com/hashicorp/go-multierror/flatten.go
  78. 46
    0
      vendor/github.com/hashicorp/go-multierror/flatten_test.go
  79. 27
    0
      vendor/github.com/hashicorp/go-multierror/format.go
  80. 40
    0
      vendor/github.com/hashicorp/go-multierror/format_test.go
  81. 3
    0
      vendor/github.com/hashicorp/go-multierror/go.mod
  82. 4
    0
      vendor/github.com/hashicorp/go-multierror/go.sum
  83. 51
    0
      vendor/github.com/hashicorp/go-multierror/multierror.go
  84. 71
    0
      vendor/github.com/hashicorp/go-multierror/multierror_test.go
  85. 37
    0
      vendor/github.com/hashicorp/go-multierror/prefix.go
  86. 33
    0
      vendor/github.com/hashicorp/go-multierror/prefix_test.go
  87. 16
    0
      vendor/github.com/hashicorp/go-multierror/sort.go
  88. 52
    0
      vendor/github.com/hashicorp/go-multierror/sort_test.go
  89. 4
    0
      vendor/github.com/hashicorp/go-retryablehttp/.gitignore
  90. 12
    0
      vendor/github.com/hashicorp/go-retryablehttp/.travis.yml
  91. 363
    0
      vendor/github.com/hashicorp/go-retryablehttp/LICENSE
  92. 11
    0
      vendor/github.com/hashicorp/go-retryablehttp/Makefile
  93. 46
    0
      vendor/github.com/hashicorp/go-retryablehttp/README.md
  94. 549
    0
      vendor/github.com/hashicorp/go-retryablehttp/client.go
  95. 673
    0
      vendor/github.com/hashicorp/go-retryablehttp/client_test.go
  96. 3
    0
      vendor/github.com/hashicorp/go-retryablehttp/go.mod
  97. 2
    0
      vendor/github.com/hashicorp/go-retryablehttp/go.sum
  98. 12
    0
      vendor/github.com/hashicorp/go-rootcerts/.travis.yml
  99. 363
    0
      vendor/github.com/hashicorp/go-rootcerts/LICENSE
  100. 0
    0
      vendor/github.com/hashicorp/go-rootcerts/Makefile

+ 249
- 0
Gopkg.lock View File

@@ -0,0 +1,249 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.


[[projects]]
digest = "1:8722889ad027febfced94665914d1e7be8f1b703d31f2ef9461c59e4d40fe974"
name = "github.com/certifi/gocertifi"
packages = ["."]
pruneopts = ""
revision = "deb3ae2ef2610fde3330947281941c562861188b"
version = "2018.01.18"

[[projects]]
digest = "1:40548207236a3c3d02b4dae094f5901d662a792e015948287dc3e9ea1bd50674"
name = "github.com/getsentry/raven-go"
packages = ["."]
pruneopts = ""
revision = "f04e7487e9a6b9d9837d52743fb5f40576c56411"
version = "v0.2.0"

[[projects]]
digest = "1:6a6322a15aa8e99bd156fbba0aae4e5d67b4bb05251d860b348a45dfdcba9cce"
name = "github.com/golang/snappy"
packages = ["."]
pruneopts = ""
revision = "2a8bb927dd31d8daada140a5d09578521ce5c36a"
version = "v0.0.1"

[[projects]]
digest = "1:8e3bd93036b4a925fe2250d3e4f38f21cadb8ef623561cd80c3c50c114b13201"
name = "github.com/hashicorp/errwrap"
packages = ["."]
pruneopts = ""
revision = "8a6fb523712970c966eefc6b39ed2c5e74880354"
version = "v1.0.0"

[[projects]]
digest = "1:984b627a3c838daa9f4c949ec8e6f049a7021b1156eb4db0337c3a5afe07aada"
name = "github.com/hashicorp/go-cleanhttp"
packages = ["."]
pruneopts = ""
revision = "eda1e5db218aad1db63ca4642c8906b26bcf2744"
version = "v0.5.1"

[[projects]]
digest = "1:72308fdd6d5ef61106a95be7ca72349a5565809042b6426a3cfb61d99483b824"
name = "github.com/hashicorp/go-multierror"
packages = ["."]
pruneopts = ""
revision = "886a7fbe3eb1c874d46f623bfa70af45f425b3d1"
version = "v1.0.0"

[[projects]]
digest = "1:0e2d55461c960fad1050dafc69c1fe2a41d5719dc6bfa7c0b74faf56dfdfe85c"
name = "github.com/hashicorp/go-retryablehttp"
packages = ["."]
pruneopts = ""
revision = "85a8ee556d7323a4faf0f4c17ee900e9ff1482e8"
version = "v0.5.4"

[[projects]]
digest = "1:1ce74de952243566df45871a9e823f3000efac179a8a75af8b1c57c49ae89d97"
name = "github.com/hashicorp/go-rootcerts"
packages = ["."]
pruneopts = ""
revision = "df8e78a645e18d56ed7bb9ae10ffb8174ab892e2"
version = "v1.0.1"

[[projects]]
digest = "1:24ee99da0190535baad44a5df2710ca2e116d615fcaaffcf3b79b476450af917"
name = "github.com/hashicorp/go-sockaddr"
packages = ["."]
pruneopts = ""
revision = "c7188e74f6acae5a989bdc959aa779f8b9f42faf"
version = "v1.0.2"

[[projects]]
digest = "1:d14365c51dd1d34d5c79833ec91413bfbb166be978724f15701e17080dc06dec"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token",
]
pruneopts = ""
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
version = "v1.0.0"

[[projects]]
digest = "1:512c0a44a36b8782ca9e33a6834838b331dbb3f218d5e6279663a0ea0760dd4d"
name = "github.com/hashicorp/vault"
packages = [
"api",
"helper/compressutil",
"helper/consts",
"helper/hclutil",
"helper/jsonutil",
"helper/parseutil",
"helper/strutil",
]
pruneopts = ""
revision = "9bc820f700f83a7c4bcab54c5323735a581b34eb"
version = "v1.1.3"

[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
pruneopts = ""
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"

[[projects]]
digest = "1:84c28d9899cc4e00c38042d345cea8819275a5a62403a58530cac67022894776"
name = "github.com/mattn/go-runewidth"
packages = ["."]
pruneopts = ""
revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3"
version = "v0.0.4"

[[projects]]
digest = "1:6dbb0eb72090871f2e58d1e37973fe3cb8c0f45f49459398d3fc740cb30e13bd"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
pruneopts = ""
revision = "af06845cf3004701891bf4fdb884bfe4920b3727"
version = "v1.1.0"

[[projects]]
digest = "1:bcc46a0fbd9e933087bef394871256b5c60269575bb661935874729c65bbbf60"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = ""
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
version = "v1.1.2"

[[projects]]
digest = "1:28a9131d18ac1b8761f41370e59b7e807148bbd24a926816233522fb64e593bf"
name = "github.com/olekukonko/tablewriter"
packages = ["."]
pruneopts = ""
revision = "e6d60cf7ba1f42d86d54cdf5508611c4aafb3970"
version = "v0.0.1"

[[projects]]
digest = "1:b21fb116e51b5c95eccbad65f2717d7d01f52c7f0d596d926e37749c7592fb88"
name = "github.com/pierrec/lz4"
packages = [
".",
"internal/xxh32",
]
pruneopts = ""
revision = "057d66e894a4e55853274a3bbbf7de02ba639e43"
version = "v2.2.4"

[[projects]]
digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = ""
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"

[[projects]]
digest = "1:4244255905cb95c3c98894d671367f84a6292608ae528936fe46ba9c86f68393"
name = "github.com/ryanuber/go-glob"
packages = ["."]
pruneopts = ""
revision = "51a8f68e6c24dc43f1e371749c89a267de4ebc53"
version = "v1.0.0"

[[projects]]
digest = "1:0c63b3c7ad6d825a898f28cb854252a3b29d37700c68a117a977263f5ec94efe"
name = "github.com/spf13/cobra"
packages = ["."]
pruneopts = ""
revision = "f2b07da1e2c38d5f12845a4f607e2e1018cbb1f5"
version = "v0.0.5"

[[projects]]
digest = "1:cbaf13cdbfef0e4734ed8a7504f57fe893d471d62a35b982bf6fb3f036449a66"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = ""
revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
version = "v1.0.3"

[[projects]]
branch = "master"
digest = "1:31cd6e3c114e17c5f0c9e8b0bcaa3025ab3c221ce36323c7ce1acaa753d0d0aa"
name = "golang.org/x/net"
packages = [
"http/httpguts",
"http2",
"http2/hpack",
"idna",
]
pruneopts = ""
revision = "da137c7871d730100384dbcf36e6f8fa493aef5b"

[[projects]]
digest = "1:740b51a55815493a8d0f2b1e0d0ae48fe48953bf7eaf3fcc4198823bf67768c0"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/language",
"internal/language/compact",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
]
pruneopts = ""
revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475"
version = "v0.3.2"

[[projects]]
branch = "master"
digest = "1:9522af4be529c108010f95b05f1022cb872f2b9ff8b101080f554245673466e1"
name = "golang.org/x/time"
packages = ["rate"]
pruneopts = ""
revision = "9d24e82272b4f38b78bc8cff74fa936d31ccd8ef"

[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/getsentry/raven-go",
"github.com/hashicorp/vault/api",
"github.com/olekukonko/tablewriter",
"github.com/spf13/cobra",
]
solver-name = "gps-cdcl"
solver-version = 1

+ 6
- 0
Gopkg.toml View File

@@ -0,0 +1,6 @@
required = [
"github.com/hashicorp/vault/api",
"github.com/spf13/cobra",
"github.com/getsentry/raven-go",
"github.com/olekukonko/tablewriter",
]

+ 23
- 28
cmd/import.go View File

@@ -6,56 +6,51 @@ import (
"github.com/spf13/cobra"
)

type userData struct {
nameFirst string
nameLast string
username string
keySecret string
keyPublic string
}

var userinfo userData
var userArgsData userArgsDataStruct

var importCmd = &cobra.Command{
Use: "import",
Short: "imports key pair and user info",
Short: "Imports key pair and user info",
Long: `This command helps you to import key pair, user's login and real name.`,
Run: func(cmd *cobra.Command, args []string) {

log.Println("Importing key pair")
log.Println("Importing key pair", userArgsData.username)

err := InitVault(VaultToken)
if err != nil {
log.Println(err)
}
c := VClient.Logical()
nameFull := userinfo.username
if userinfo.nameFirst != "" && userinfo.nameLast != "" {
nameFull = userinfo.nameFirst + " " + userinfo.nameLast
nameFull := userArgsData.username
if userArgsData.nameFirst != "" && userArgsData.nameLast != "" {
nameFull = userArgsData.nameFirst + " " + userArgsData.nameLast
}

user := map[string]interface{}{
"name-first": userArgsData.nameFirst,
"name-last": userArgsData.nameLast,
"name": nameFull,
"username": userArgsData.username,
"key-secret": readFileContents(userArgsData.keySecret),
"key-public": readFileContents(userArgsData.keyPublic),
}
secret, err := c.Write("ssh-keys/"+userinfo.username,
map[string]interface{}{
"name-first": userinfo.nameFirst,
"name-last": userinfo.nameLast,
"name": nameFull,
"username": userinfo.username,
"key-secret": userinfo.keySecret,
"key-public": userinfo.keyPublic,
})

secret, err := c.Write("ssh-keys/"+nameFull, user)
if err != nil {
log.Println(err)
}

log.Println(secret)
},
}

func init() {
importCmd.Flags().StringVarP(&userinfo.username, "username", "u", "", "User's login, required")
importCmd.Flags().StringVarP(&userArgsData.username, "username", "u", "", "User's login, required")
importCmd.MarkFlagRequired("username")
importCmd.Flags().StringVarP(&userinfo.keyPublic, "key-public", "p", "", "Public key file, required")
importCmd.Flags().StringVarP(&userArgsData.keyPublic, "key-public", "p", "", "Public key file, required")
importCmd.MarkFlagRequired("key-public")
importCmd.Flags().StringVarP(&userinfo.keySecret, "key-secret", "s", "", "Secret key file")
importCmd.Flags().StringVarP(&userinfo.nameFirst, "name-first", "f", "", "First name")
importCmd.Flags().StringVarP(&userinfo.nameLast, "name-last", "l", "", "Last name")
importCmd.Flags().StringVarP(&userArgsData.keySecret, "key-secret", "s", "", "Secret key file")
importCmd.Flags().StringVarP(&userArgsData.nameFirst, "name-first", "f", "", "First name")
importCmd.Flags().StringVarP(&userArgsData.nameLast, "name-last", "l", "", "Last name")
rootCmd.AddCommand(importCmd)
}

+ 53
- 0
cmd/list.go View File

@@ -0,0 +1,53 @@
package cmd

import (
"fmt"
"log"

"github.com/spf13/cobra"
)

var listDetailed bool

var listCmd = &cobra.Command{
Use: "list",
Short: "List all users in vault",
Long: `List users from Vault in brief or detailed view`,
Run: func(cmd *cobra.Command, args []string) {

fmt.Println("Listing users in vault")

err := InitVault(VaultToken)
if err != nil {
log.Fatalln(err)
}
c := VClient.Logical()

vaultData, err := c.List("ssh-keys/")
if err != nil {
log.Fatalln(err)
}

for num, value := range vaultData.Data["keys"].([]interface{}) {
if listDetailed {
userData, err := c.Read("ssh-keys/" + value.(string))
if err != nil {
log.Fatalln(err)
}
for propName, propValue := range userData.Data {
if propName != "key-public" && propName != "key-secret" {
fmt.Printf("%s -> %v\t", propName, propValue)
}
}
fmt.Printf("\n")
} else {
fmt.Printf("%d: %v\n", num, value)
}
}
},
}

func init() {
listCmd.Flags().BoolVarP(&listDetailed, "detailed", "d", false, "Show detailed info")
rootCmd.AddCommand(listCmd)
}

+ 26
- 2
cmd/root.go View File

@@ -2,19 +2,31 @@ package cmd

import (
"fmt"
"io/ioutil"
"log"
"os"

"github.com/hashicorp/vault/api"
"github.com/spf13/cobra"
)

type userArgsDataStruct struct {
nameFirst string
nameLast string
username string
keySecret string
keyPublic string
}

var VClient *api.Client // global variable

var VaultToken = "a.352sdflgslfh3lkj4h53lk4j"
// set here token
var VaultToken = ""

func InitVault(token string) error {
conf := &api.Config{
Address: "https://vault.server.ru",
// set here vault address
Address: "",
}

client, err := api.NewClient(conf)
@@ -45,3 +57,15 @@ func Execute() {
func init() {
cobra.OnInitialize()
}

func readFileContents(filename string) []byte {
b, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatalln(err)
}
return b
}

func generateSecretPath(str string) string {
return ""
}

+ 27
- 0
keys/johnny_gomez.pem View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAxUIy6MqsQ1vt87WrT12IY6mMa2gZS+/4j2ETcuBB1TTutt1Z
y5LOZdiih7TJkZ9Lpqucta3EdJ7eFrmks1UvRfcAiSu0Lerbh77YVwu1e2t6edAy
E08H2lkXg24tRx7nj24XcC64b6i0usD1Zmp6yzVCwjIc+N70VbFspIlXEMk1DF5o
VmD4rEAdYoaKO2a7EbChm6pJHrcgKApbqjxD6GoIUmh8yCS+MjUqWzSc7nkkDsiy
8npQbfMI+fWIFRzt9z70MxVdQiqWMJgQLIcCcmSxqk7SxikblNdO32tCdhX60qsY
eh79tF5Y0eBmk7spYAlieU1A5IXoONthLGtvqQIDAQABAoIBAH9Cwyduwg4++9g0
R05PsJNjqkUNBuz356tQ3MRLLA3KNYviaHEuyFvqlIjquPRrCdqAcvy/G7SFXhDZ
Ogm8lmAUB9N3T6okD/eWx2n5Et+KKpz2FahhX1aeDc5sQWfV51ygkRx9992JwPeP
T4MAhTIerXAt9oUVChtS+R15waMAmmhN7rIT0AlX4QJ6Db+BlODf8iuh3YveAhk0
ihA4EL2WW7+WQzVSyYovZLrV0WsPFde6dLVx3GjvoU0s05m2mJWwStHRBpwKm4I8
Iox1KjPTpQEvrWtCvRxbn5u7k3CDzYdQBOCHWcl9z7MCorpbxIyJfv1H2jZRq1Gv
jN9kMfUCgYEA5Oa4fhz/LbTJZymmgpHuv0WQM1Rt6TL7CvwAcTKBatg6CKzytgor
6gFcRFHSvx81OaqarK521x+KUqNt5nzzJ/Gj5dJhR57h5wWtcrmOwGJ7vxluKDnr
c/gTWSgzMaBAh0lMc1WuKdbO3dOTbpZOzPtHmzBH+geOyfW3WOIYO4cCgYEA3Jx8
/0CNxIR18NLgc9Bw5COHG0nkxhcHWWhT7u52W2fODJqRSk7LilLCnJRMdiES4QHz
vS+KATfQArOQ5XVlTtCODbeVbUYIok1jYL8xyT116izTO6+kUpEqjSX7rMMd/iWV
nvHuSSWC4SAoG+5LqnrFjCA2c0kgsTI/tIYEp08CgYEAmlCI1EyMI64bkTbdEG19
fOlSu6gRqQM7hz44theMdBFtnmeqANt+m3VrVIus/jf10/iV6hB8Aa87u1Z+QsK9
0pHvIoLgHj6YoPyM4CkEeeJq9nwYbXoOp9H4iUS2rqAQoKPjrGV40rAEKjv2yrj+
SuakcZTtETyDINhMUNmr6IUCgYBoeGatDanU5RFg8Mu/rHa5YqWpY43FzMP7L++H
Vz+PO5IU3+FrufKjV1u3gHbQ/gqNqTn+XhPRTdfgX55/Ot+1ugd7MXO+ooqWKWWl
PAVckhFTytRhEGUVgI8Q9mq/Pkrmvm+jLFg2JWBcMbksuOT2H1uLZvnCEGA96VjH
qSts+wKBgESHdvvilzy6G3Cj5YjY8WkCaATQmfztMZFfn9qEGjH6kbe8Mvd7ASoH
CGTuyTUN0CTtSuGcHmK4jVC1UBsxSY6BGdwRk2NNB2Gs4Sv8FSx4OmD+CZbs4V64
yA4k8oYaJAwfssvf8UNy5rUqi1I1Ogr9XTXfYPhvFk+9QGJgOVaV
-----END RSA PRIVATE KEY-----

+ 1
- 0
keys/johnny_gomez.pem.pub View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFQjLoyqxDW+3ztatPXYhjqYxraBlL7/iPYRNy4EHVNO623VnLks5l2KKHtMmRn0umq5y1rcR0nt4WuaSzVS9F9wCJK7Qt6tuHvthXC7V7a3p50DITTwfaWReDbi1HHuePbhdwLrhvqLS6wPVmanrLNULCMhz43vRVsWykiVcQyTUMXmhWYPisQB1ihoo7ZrsRsKGbqkketyAoCluqPEPoaghSaHzIJL4yNSpbNJzueSQOyLLyelBt8wj59YgVHO33PvQzFV1CKpYwmBAshwJyZLGqTtLGKRuU107fa0J2FfrSqxh6Hv20XljR4GaTuylgCWJ5TUDkheg422Esa2+p vlad@turtle

+ 27
- 0
keys/nick_diamond.pem View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAy1rC9jEy+qAkZJCIcfk44F6IBkrfZ7sQSmBYkF2d3P7K453k
huleDbU1SRI8q749vLv4XjaprIGyOXAozIG3HwDgGajCgZrDflNAjtlng08SEEyk
aJTW6kkE0ZV4D03nF3UHPK7WhfOtQolxmXWnIeHpb4aabxhKnXhqY2Q+X5Wk5WzG
g5wmxuIKDZE1BaO6ZHicH6MICfRZwLm7fzSahk5LfU2sSdCDii050/z/D1pyEFBU
KcVTSr6Y5xr8CzlihON1YTV6Fke6t6sIdGhrCWTN1B0Wcoqczr7qNaY3gOq39Q9U
iS/tjkM/ubRFGorY1LTGA+Su2C5sws8eJrgagQIDAQABAoIBAQCGGdpLo/IsWILn
VnDESEd7npsxGxrsOu3pPlPpD/NUkymUpMDZPvfpLkBvaXldAF2SIpsM9hs6SWUQ
f6hIBJqP9XHTOfjaowacPaZrDW83pmMRIJmi5dQ8N97XnyvHW+YzD9ev6tRJDYKK
SD0DqBSJQYNBKpCZBnneElSGKsr/GG+bk796QY4EtoulisEHInrYGxpvJGkqQJII
/FUhWVwyr732DNI2F5GquDACZakEtz1EEP3e7+I0+DkVhspmuZasOjEKi1y1RPt4
CjIxUO6nKjOsjB3CNe7uYyRCes+Gdc+JCvj3jkB18adHfvZy6+n8MGb47047Nn60
mZGwcv4BAoGBAPq21/MQzYAU0MBJ8jRg/U4xnXaAgpiS6t1ab/MtIA9Bivot+ftd
8Ahq4C6QidyTuirP6vOvTihvnL/KKoOyAEunfeqYh/TM//S08orh60wimkoyWRkK
SxIe1Yl23W+jtK4hzKtYsun59QcOxd3QrPBQj5D99EHiQMqImgXr5aSRAoGBAM+k
TtcJvh+CQwsFuXocOVMUaOdpd45JZ4BY4x3PUqwDYVPz9XiVeocfGbT9o0i172Fa
uux/XBcmRlAFox89KGB7j0h79G3KQnMoukEbw58z+wKvhWabG90p2fwku4YkSOAA
JTWbls37kw55WbmaAlIAIgNvmaPbL3q2H+AHJE7xAoGAXLY+t0DQbXvFEkaGDX6M
TV0lmIu/P4Kc3Ywy998JCvs21HsqBR1gxQwYqL+Ea5ApeOl4PfLm2Sck9Ptiq16o
o1Fly9np9MnWIPzE8DzJEVd+bM+fiRX4WtQSLQpKqp0EFJylHfvn9YQcaya4XuVq
/lccMAbpS2/Ke9r18higzDECgYASMUUvFg1wWUtiddfwWPS00L74N4dgEhJdbmxH
ulh19b3bSy6gqAgaOqFX+DkML9SPZRGnkukQpivW6weFvygAfBLruOCLlgMIb/MR
rUsQcc92RsabXM+uKvKMYvODJz6zUY/xCHbICVizFfLEjjfR020ne+msL5ITYE2e
pUGLIQKBgGp5lzVuMFmGGljBzrVsvrLZhdIkZlWa0NUe1Tuvz4/MB8+btf0GAMh9
pnH/v09afrMxCnzI1haE4FQudL6gVSqVYIk/Qj2g73IO3HzLHpU4Ke5vAzTROMd+
8T6qR7Ofa0EKWl0MaG6vogmb7/RbvQQLkaZD0kLi2UxJ/DFpVUVt
-----END RSA PRIVATE KEY-----

+ 1
- 0
keys/nick_diamond.pem.pub View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLWsL2MTL6oCRkkIhx+TjgXogGSt9nuxBKYFiQXZ3c/srjneSG6V4NtTVJEjyrvj28u/heNqmsgbI5cCjMgbcfAOAZqMKBmsN+U0CO2WeDTxIQTKRolNbqSQTRlXgPTecXdQc8rtaF861CiXGZdach4elvhppvGEqdeGpjZD5flaTlbMaDnCbG4goNkTUFo7pkeJwfowgJ9FnAubt/NJqGTkt9TaxJ0IOKLTnT/P8PWnIQUFQpxVNKvpjnGvwLOWKE43VhNXoWR7q3qwh0aGsJZM3UHRZyipzOvuo1pjeA6rf1D1SJL+2OQz+5tEUaitjUtMYD5K7YLmzCzx4muBqB vlad@turtle

+ 27
- 0
keys/stacy_combred.pem View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAx6nM7os3akHfYbnoHnO3uV7tgmqq+Y3mAmdLQnVCtqj+D//g
W8QfLZzJFFWayP/MOFIaGUiRQI6VFQwSK1bnG1XrJwf19iBHZjDGx1TIbEenqeA+
FoDs459s8EYRhgh43a4eDj7d4gZu/F46+po5JS6UigiwlS5sMchTDn+ZfOkmN1tE
9TCMevYRrcMhPkQiJFJu7JlFAHz1LeliRQNHfKm30giTPieGRTBGF1bdSMH36yhx
+ki2o8UMGSr00S9pIQyDFl9qlr/oEOMOCLOUCipdwD1yl7HAkpdPz9+kbJC8ioAp
ak44Wy3xT544NHPIpdwp6f/UJtHMOxVEQX0yTwIDAQABAoIBAQCsWQM/6kY0X5Yu
amZWWvb9eVa1m9PoRLeF3ZoWTNgAilF8iBgnvlxXaB4egmJxHHUxLJXAu2Y+fpVr
rJbGeGZXr0ujznhfM6/OI06C48l5xkSRG7Mcg794ILbNlP4tX4TNfi4Nh/DJ2VnW
WJS6m4O2d8XafPPIYmEce9pOCpD4Q1S92H7OLXfptOXX95eX1iagNsFgRC7CjzNn
1aes9+hkkoxkfNpS8M/wJk8YiEf2WldHY0dWjbqypCi1gFPbV2ZRN7F+4ro0ovA4
iEvamJByR5kgYAX6cL6vM1/Pz9PSRiW3FB6FmM4J1qcdhdfTXRga4lUUZcNc+YBR
I//vi91hAoGBAProaEuztDQ1JENm0mZsGVctB8lrkDQ/cAqC2HnPJ1oSvlUW370S
sXEsmBoiBhIsl3ki+M/4Ysio2peANYkbwdUDrwxgS3rvjz1Gn1iYzhgniEYjRNGo
s4D4dnKhGBToteUDrBogUnt3vmb5V9cizczs8sWyLci2cxJRpAqn/98/AoGBAMu3
Jt3f8SIMycjTIuPZjI8/p1p3NDbEhqtvrQhcX+M9HWiJcGelJUZgagRnSIKH1bO7
zD7F7cMBn3qapX0thNlhZxlu8/N1C3TUFP86FsAi6P9DQbgv2c9t88dML70oGavI
wzWYyiyNLjx15v+FWxVJJzmkDpgXL7Bmvvy36fjxAoGAV/Ox4595srdSH+JiLI8G
Dv309dEjunzmTHjzYzePZs6dM3ySL1fnJXHpW3fDwwkoi8kLiPNbxVUTUvoZSheV
yZLCAMecjdp9zo/7+XWPAP6SkWt5izjku3fdJW983o94e2/fG0VrRGaEMtqCxSIp
HAWPUGeWDv6NtcUyiJ29XscCgYAKYprjor/7OWWsDD9yGCwXPWp9rSgHRruKz9Wl
YHgu8HWoV6FYV03wKYgeE1nZ9azKTd+ar6JwV4vWxz16hTCJmHKWFFbxaV0dJpav
cDmqr9zq7nXSOvZCodtUpKimXv34CPMSr2hXHxdlU6lkC1taWXFbeeaYkE4JEyPX
ZFTUMQKBgG8xvFpBaB+UVRtz58iIt6p3SvEs9lklIqy9n04c6gTDmHeA5bHKLoS7
MBhJmT3wAAyS/pmcGT6IRSGBKJfj7jnPUomvLCHIz1EdJRL/sACHVx2fMgUpi7Sn
F7QMbNk/oTmln+Ik3a2kYQ33Mmi/oWhwwNAYJB9gXumrJmWe/lHu
-----END RSA PRIVATE KEY-----

+ 1
- 0
keys/stacy_combred.pem.pub View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHqczuizdqQd9huegec7e5Xu2Caqr5jeYCZ0tCdUK2qP4P/+BbxB8tnMkUVZrI/8w4UhoZSJFAjpUVDBIrVucbVesnB/X2IEdmMMbHVMhsR6ep4D4WgOzjn2zwRhGGCHjdrh4OPt3iBm78Xjr6mjklLpSKCLCVLmwxyFMOf5l86SY3W0T1MIx69hGtwyE+RCIkUm7smUUAfPUt6WJFA0d8qbfSCJM+J4ZFMEYXVt1IwffrKHH6SLajxQwZKvTRL2khDIMWX2qWv+gQ4w4Is5QKKl3APXKXscCSl0/P36RskLyKgClqTjhbLfFPnjg0c8il3Cnp/9Qm0cw7FURBfTJP vlad@turtle

+ 3
- 0
vendor/github.com/certifi/gocertifi/LICENSE View File

@@ -0,0 +1,3 @@
This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
one at http://mozilla.org/MPL/2.0/.

+ 60
- 0
vendor/github.com/certifi/gocertifi/README.md View File

@@ -0,0 +1,60 @@
# GoCertifi: SSL Certificates for Golang

This Go package contains a CA bundle that you can reference in your Go code.
This is useful for systems that do not have CA bundles that Golang can find
itself, or where a uniform set of CAs is valuable.

This is the same CA bundle that ships with the
[Python Requests](https://github.com/kennethreitz/requests) library, and is a
Golang specific port of [certifi](https://github.com/kennethreitz/certifi). The
CA bundle is derived from Mozilla's canonical set.

## Usage

You can use the `gocertifi` package as follows:

```go
import "github.com/certifi/gocertifi"

cert_pool, err := gocertifi.CACerts()
```

You can use the returned `*x509.CertPool` as part of an HTTP transport, for example:

```go
import (
"net/http"
"crypto/tls"
)

// Setup an HTTP client with a custom transport
transport := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: cert_pool},
}
client := &http.Client{Transport: transport}

// Make an HTTP request using our custom transport
resp, err := client.Get("https://example.com")
```

## Detailed Documentation

Import as follows:

```go
import "github.com/certifi/gocertifi"
```

### Errors

```go
var ErrParseFailed = errors.New("gocertifi: error when parsing certificates")
```

### Functions

```go
func CACerts() (*x509.CertPool, error)
```
CACerts builds an X.509 certificate pool containing the Mozilla CA Certificate
bundle. Returns nil on error along with an appropriate error code.

+ 4456
- 0
vendor/github.com/certifi/gocertifi/certifi.go
File diff suppressed because it is too large
View File


+ 10
- 0
vendor/github.com/certifi/gocertifi/certifi_test.go View File

@@ -0,0 +1,10 @@
package gocertifi

import "testing"

func TestGetCerts(t *testing.T) {
cert_pool, err := CACerts()
if (cert_pool == nil) || (err != nil) {
t.Errorf("Failed to return the certificates.")
}
}

+ 20
- 0
vendor/github.com/certifi/gocertifi/tasks.py View File

@@ -0,0 +1,20 @@
from invoke import task
import requests

@task
def update(ctx):
r = requests.get('https://mkcert.org/generate/')
r.raise_for_status()
certs = r.content

with open('certifi.go', 'rb') as f:
file = f.read()

file = file.split('`\n')
assert len(file) == 3
file[1] = certs

ctx.run("rm certifi.go")

with open('certifi.go', 'wb') as f:
f.write('`\n'.join(file))

+ 1
- 0
vendor/github.com/getsentry/raven-go/.dockerignore View File

@@ -0,0 +1 @@
.git

+ 5
- 0
vendor/github.com/getsentry/raven-go/.gitignore View File

@@ -0,0 +1,5 @@
*.test
*.out
example/example
/xunit.xml
/coverage.xml

+ 0
- 0
vendor/github.com/getsentry/raven-go/.gitmodules View File


+ 41
- 0
vendor/github.com/getsentry/raven-go/.travis.yml View File

@@ -0,0 +1,41 @@
sudo: false
language: go
go:
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- tip

before_install:
- go install -race std
- go get golang.org/x/tools/cmd/cover
- go get github.com/tebeka/go2xunit
- go get github.com/t-yuki/gocover-cobertura
- go get -v ./...

script:
- go test -v -race ./... | tee gotest.out
- $GOPATH/bin/go2xunit -fail -input gotest.out -output xunit.xml
- go test -v -coverprofile=coverage.txt -covermode count .
- $GOPATH/bin/gocover-cobertura < coverage.txt > coverage.xml
after_script:
- npm install -g @zeus-ci/cli
- zeus upload -t "application/x-cobertura+xml" coverage.xml
- zeus upload -t "application/x-xunit+xml" xunit.xml

matrix:
allow_failures:
- go: tip

notifications:
webhooks:
urls:
- https://zeus.ci/hooks/cd949996-d30a-11e8-ba53-0a580a28042d/public/provider/travis/webhook
on_success: always
on_failure: always
on_start: always
on_cancel: always
on_error: always

+ 28
- 0
vendor/github.com/getsentry/raven-go/LICENSE View File

@@ -0,0 +1,28 @@
Copyright (c) 2013 Apollic Software, LLC. All rights reserved.
Copyright (c) 2015 Functional Software, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Apollic Software, LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 19
- 0
vendor/github.com/getsentry/raven-go/README.md View File

@@ -0,0 +1,19 @@
# raven

[![Build Status](https://api.travis-ci.org/getsentry/raven-go.svg?branch=master)](https://travis-ci.org/getsentry/raven-go)
[![Go Report Card](https://goreportcard.com/badge/github.com/getsentry/raven-go)](https://goreportcard.com/report/github.com/getsentry/raven-go)
[![GoDoc](https://godoc.org/github.com/getsentry/raven-go?status.svg)](https://godoc.org/github.com/getsentry/raven-go)

raven is the official Go SDK for the [Sentry](https://github.com/getsentry/sentry)
event/error logging system.

- [**API Documentation**](https://godoc.org/github.com/getsentry/raven-go)
- [**Usage and Examples**](https://docs.sentry.io/clients/go/)

## Installation

```text
go get github.com/getsentry/raven-go
```

Note: Go 1.7 and newer are supported.

+ 977
- 0
vendor/github.com/getsentry/raven-go/client.go View File

@@ -0,0 +1,977 @@
// Package raven implements a client for the Sentry error logging service.
package raven

import (
"bytes"
"compress/zlib"
"crypto/rand"
"crypto/tls"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
mrand "math/rand"
"net/http"
"net/url"
"os"
"regexp"
"runtime"
"strings"
"sync"
"time"

"github.com/certifi/gocertifi"
pkgErrors "github.com/pkg/errors"
)

const (
userAgent = "raven-go/1.0"
timestampFormat = `"2006-01-02T15:04:05.00"`
)

var (
ErrPacketDropped = errors.New("raven: packet dropped")
ErrUnableToUnmarshalJSON = errors.New("raven: unable to unmarshal JSON")
ErrMissingUser = errors.New("raven: dsn missing public key and/or password")
ErrMissingProjectID = errors.New("raven: dsn missing project id")
ErrInvalidSampleRate = errors.New("raven: sample rate should be between 0 and 1")
)

type Severity string

// http://docs.python.org/2/howto/logging.html#logging-levels
const (
DEBUG = Severity("debug")
INFO = Severity("info")
WARNING = Severity("warning")
ERROR = Severity("error")
FATAL = Severity("fatal")
)

type Timestamp time.Time

func (t Timestamp) MarshalJSON() ([]byte, error) {
return []byte(time.Time(t).UTC().Format(timestampFormat)), nil
}

func (timestamp *Timestamp) UnmarshalJSON(data []byte) error {
t, err := time.Parse(timestampFormat, string(data))
if err != nil {
return err
}

*timestamp = Timestamp(t)
return nil
}

func (timestamp Timestamp) Format(format string) string {
t := time.Time(timestamp)
return t.Format(format)
}

// An Interface is a Sentry interface that will be serialized as JSON.
// It must implement json.Marshaler or use json struct tags.
type Interface interface {
// The Sentry class name. Example: sentry.interfaces.Stacktrace
Class() string
}

type Culpriter interface {
Culprit() string
}

type Transport interface {
Send(url, authHeader string, packet *Packet) error
}

type Extra map[string]interface{}

type outgoingPacket struct {
packet *Packet
ch chan error
}

type Tag struct {
Key string
Value string
}

type Tags []Tag

func (tag *Tag) MarshalJSON() ([]byte, error) {
return json.Marshal([2]string{tag.Key, tag.Value})
}

func (t *Tag) UnmarshalJSON(data []byte) error {
var tag [2]string
if err := json.Unmarshal(data, &tag); err != nil {
return err
}
*t = Tag{tag[0], tag[1]}
return nil
}

func (t *Tags) UnmarshalJSON(data []byte) error {
var tags []Tag

switch data[0] {
case '[':
// Unmarshal into []Tag
if err := json.Unmarshal(data, &tags); err != nil {
return err
}
case '{':
// Unmarshal into map[string]string
tagMap := make(map[string]string)
if err := json.Unmarshal(data, &tagMap); err != nil {
return err
}

// Convert to []Tag
for k, v := range tagMap {
tags = append(tags, Tag{k, v})
}
default:
return ErrUnableToUnmarshalJSON
}

*t = tags
return nil
}

// https://docs.getsentry.com/hosted/clientdev/#building-the-json-packet
type Packet struct {
// Required
Message string `json:"message"`

// Required, set automatically by Client.Send/Report via Packet.Init if blank
EventID string `json:"event_id"`
Project string `json:"project"`
Timestamp Timestamp `json:"timestamp"`
Level Severity `json:"level"`
Logger string `json:"logger"`

// Optional
Platform string `json:"platform,omitempty"`
Culprit string `json:"culprit,omitempty"`
ServerName string `json:"server_name,omitempty"`
Release string `json:"release,omitempty"`
Environment string `json:"environment,omitempty"`
Tags Tags `json:"tags,omitempty"`
Modules map[string]string `json:"modules,omitempty"`
Fingerprint []string `json:"fingerprint,omitempty"`
Extra Extra `json:"extra,omitempty"`

Interfaces []Interface `json:"-"`
}

// NewPacket constructs a packet with the specified message and interfaces.
func NewPacket(message string, interfaces ...Interface) *Packet {
extra := Extra{}
setExtraDefaults(extra)
return &Packet{
Message: message,
Interfaces: interfaces,
Extra: extra,
}
}

// NewPacketWithExtra constructs a packet with the specified message, extra information, and interfaces.
func NewPacketWithExtra(message string, extra Extra, interfaces ...Interface) *Packet {
if extra == nil {
extra = Extra{}
}
setExtraDefaults(extra)

return &Packet{
Message: message,
Interfaces: interfaces,
Extra: extra,
}
}

func setExtraDefaults(extra Extra) Extra {
extra["runtime.Version"] = runtime.Version()
extra["runtime.NumCPU"] = runtime.NumCPU()
extra["runtime.GOMAXPROCS"] = runtime.GOMAXPROCS(0) // 0 just returns the current value
extra["runtime.NumGoroutine"] = runtime.NumGoroutine()
return extra
}

// Init initializes required fields in a packet. It is typically called by
// Client.Send/Report automatically.
func (packet *Packet) Init(project string) error {
if packet.Project == "" {
packet.Project = project
}
if packet.EventID == "" {
var err error
packet.EventID, err = uuid()
if err != nil {
return err
}
}
if time.Time(packet.Timestamp).IsZero() {
packet.Timestamp = Timestamp(time.Now())
}
if packet.Level == "" {
packet.Level = ERROR
}
if packet.Logger == "" {
packet.Logger = "root"
}
if packet.ServerName == "" {
packet.ServerName = hostname
}
if packet.Platform == "" {
packet.Platform = "go"
}

if packet.Culprit == "" {
for _, inter := range packet.Interfaces {
if c, ok := inter.(Culpriter); ok {
packet.Culprit = c.Culprit()
if packet.Culprit != "" {
break
}
}
}
}

return nil
}

func (packet *Packet) AddTags(tags map[string]string) {
for k, v := range tags {
packet.Tags = append(packet.Tags, Tag{k, v})
}
}

func uuid() (string, error) {
id := make([]byte, 16)
_, err := io.ReadFull(rand.Reader, id)
if err != nil {
return "", err
}
id[6] &= 0x0F // clear version
id[6] |= 0x40 // set version to 4 (random uuid)
id[8] &= 0x3F // clear variant
id[8] |= 0x80 // set to IETF variant
return hex.EncodeToString(id), nil
}

func (packet *Packet) JSON() ([]byte, error) {
packetJSON, err := json.Marshal(packet)
if err != nil {
return nil, err
}

interfaces := make(map[string]Interface, len(packet.Interfaces))
for _, inter := range packet.Interfaces {
if inter != nil {
interfaces[inter.Class()] = inter
}
}

if len(interfaces) > 0 {
interfaceJSON, err := json.Marshal(interfaces)
if err != nil {
return nil, err
}
packetJSON[len(packetJSON)-1] = ','
packetJSON = append(packetJSON, interfaceJSON[1:]...)
}

return packetJSON, nil
}

type context struct {
user *User
http *Http
tags map[string]string
}

func (c *context) setUser(u *User) { c.user = u }
func (c *context) setHttp(h *Http) { c.http = h }
func (c *context) setTags(t map[string]string) {
if c.tags == nil {
c.tags = make(map[string]string)
}
for k, v := range t {
c.tags[k] = v
}
}
func (c *context) clear() {
c.user = nil
c.http = nil
c.tags = nil
}

// Return a list of interfaces to be used in appending with the rest
func (c *context) interfaces() []Interface {
len, i := 0, 0
if c.user != nil {
len++
}
if c.http != nil {
len++
}
interfaces := make([]Interface, len)
if c.user != nil {
interfaces[i] = c.user
i++
}
if c.http != nil {
interfaces[i] = c.http
i++
}
return interfaces
}

// The maximum number of packets that will be buffered waiting to be delivered.
// Packets will be dropped if the buffer is full. Used by NewClient.
var MaxQueueBuffer = 100

func newTransport() Transport {
t := &HTTPTransport{}
rootCAs, err := gocertifi.CACerts()
if err != nil {
log.Println("raven: failed to load root TLS certificates:", err)
} else {
t.Client = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{RootCAs: rootCAs},
},
}
}
return t
}

func newClient(tags map[string]string) *Client {
client := &Client{
Transport: newTransport(),
Tags: tags,
context: &context{},
sampleRate: 1.0,
queue: make(chan *outgoingPacket, MaxQueueBuffer),
}
client.SetDSN(os.Getenv("SENTRY_DSN"))
client.SetRelease(os.Getenv("SENTRY_RELEASE"))
client.SetEnvironment(os.Getenv("SENTRY_ENVIRONMENT"))
return client
}

// New constructs a new Sentry client instance
func New(dsn string) (*Client, error) {
client := newClient(nil)
return client, client.SetDSN(dsn)
}

// NewWithTags constructs a new Sentry client instance with default tags.
func NewWithTags(dsn string, tags map[string]string) (*Client, error) {
client := newClient(tags)
return client, client.SetDSN(dsn)
}

// NewClient constructs a Sentry client and spawns a background goroutine to
// handle packets sent by Client.Report.
//
// Deprecated: use New and NewWithTags instead
func NewClient(dsn string, tags map[string]string) (*Client, error) {
client := newClient(tags)
return client, client.SetDSN(dsn)
}

// Client encapsulates a connection to a Sentry server. It must be initialized
// by calling NewClient. Modification of fields concurrently with Send or after
// calling Report for the first time is not thread-safe.
type Client struct {
Tags map[string]string

Transport Transport

// DropHandler is called when a packet is dropped because the buffer is full.
DropHandler func(*Packet)

// Context that will get appending to all packets
context *context

mu sync.RWMutex
url string
projectID string
authHeader string
release string
environment string
sampleRate float32

// default logger name (leave empty for 'root')
defaultLoggerName string

includePaths []string
ignoreErrorsRegexp *regexp.Regexp
queue chan *outgoingPacket

// A WaitGroup to keep track of all currently in-progress captures
// This is intended to be used with Client.Wait() to assure that
// all messages have been transported before exiting the process.
wg sync.WaitGroup

// A Once to track only starting up the background worker once
start sync.Once
}

// Initialize a default *Client instance
var DefaultClient = newClient(nil)

func (c *Client) SetIgnoreErrors(errs []string) error {
joinedRegexp := strings.Join(errs, "|")
r, err := regexp.Compile(joinedRegexp)
if err != nil {
return fmt.Errorf("failed to compile regexp %q for %q: %v", joinedRegexp, errs, err)
}

c.mu.Lock()
c.ignoreErrorsRegexp = r
c.mu.Unlock()
return nil
}

func (c *Client) shouldExcludeErr(errStr string) bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.ignoreErrorsRegexp != nil && c.ignoreErrorsRegexp.MatchString(errStr)
}

func SetIgnoreErrors(errs ...string) error {
return DefaultClient.SetIgnoreErrors(errs)
}

// SetDSN updates a client with a new DSN. It safe to call after and
// concurrently with calls to Report and Send.
func (client *Client) SetDSN(dsn string) error {
if dsn == "" {
return nil
}

client.mu.Lock()
defer client.mu.Unlock()

uri, err := url.Parse(dsn)
if err != nil {
return err
}

if uri.User == nil {
return ErrMissingUser
}
publicKey := uri.User.Username()
secretKey, hasSecretKey := uri.User.Password()
uri.User = nil

if idx := strings.LastIndex(uri.Path, "/"); idx != -1 {
client.projectID = uri.Path[idx+1:]
uri.Path = uri.Path[:idx+1] + "api/" + client.projectID + "/store/"
}
if client.projectID == "" {
return ErrMissingProjectID
}

client.url = uri.String()

if hasSecretKey {
client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s, sentry_secret=%s", publicKey, secretKey)
} else {
client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s", publicKey)
}

return nil
}

// Sets the DSN for the default *Client instance
func SetDSN(dsn string) error { return DefaultClient.SetDSN(dsn) }

// SetRelease sets the "release" tag.
func (client *Client) SetRelease(release string) {
client.mu.Lock()
defer client.mu.Unlock()
client.release = release
}

// SetEnvironment sets the "environment" tag.
func (client *Client) SetEnvironment(environment string) {
client.mu.Lock()
defer client.mu.Unlock()
client.environment = environment
}

// SetDefaultLoggerName sets the default logger name.
func (client *Client) SetDefaultLoggerName(name string) {
client.mu.Lock()
defer client.mu.Unlock()
client.defaultLoggerName = name
}

// SetSampleRate sets how much sampling we want on client side
func (client *Client) SetSampleRate(rate float32) error {
client.mu.Lock()
defer client.mu.Unlock()

if rate < 0 || rate > 1 {
return ErrInvalidSampleRate
}
client.sampleRate = rate
return nil
}

// SetRelease sets the "release" tag on the default *Client
func SetRelease(release string) { DefaultClient.SetRelease(release) }

// SetEnvironment sets the "environment" tag on the default *Client
func SetEnvironment(environment string) { DefaultClient.SetEnvironment(environment) }

// SetDefaultLoggerName sets the "defaultLoggerName" on the default *Client
func SetDefaultLoggerName(name string) {
DefaultClient.SetDefaultLoggerName(name)
}

// SetSampleRate sets the "sample rate" on the degault *Client
func SetSampleRate(rate float32) error { return DefaultClient.SetSampleRate(rate) }

func (client *Client) worker() {
for outgoingPacket := range client.queue {

client.mu.RLock()
url, authHeader := client.url, client.authHeader
client.mu.RUnlock()

outgoingPacket.ch <- client.Transport.Send(url, authHeader, outgoingPacket.packet)
client.wg.Done()
}
}

// Capture asynchronously delivers a packet to the Sentry server. It is a no-op
// when client is nil. A channel is provided if it is important to check for a
// send's success.
func (client *Client) Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) {
ch = make(chan error, 1)

if client == nil {
// return a chan that always returns nil when the caller receives from it
close(ch)
return
}

if client.sampleRate < 1.0 && mrand.Float32() > client.sampleRate {
return
}

if packet == nil {
close(ch)
return
}

if client.shouldExcludeErr(packet.Message) {
return
}

// Keep track of all running Captures so that we can wait for them all to finish
// *Must* call client.wg.Done() on any path that indicates that an event was
// finished being acted upon, whether success or failure
client.wg.Add(1)

// Merge capture tags and client tags
packet.AddTags(captureTags)
packet.AddTags(client.Tags)

// Initialize any required packet fields
client.mu.RLock()
packet.AddTags(client.context.tags)
projectID := client.projectID
release := client.release
environment := client.environment
defaultLoggerName := client.defaultLoggerName
client.mu.RUnlock()

// set the global logger name on the packet if we must
if packet.Logger == "" && defaultLoggerName != "" {
packet.Logger = defaultLoggerName
}

err := packet.Init(projectID)
if err != nil {
ch <- err
client.wg.Done()
return
}

if packet.Release == "" {
packet.Release = release
}

if packet.Environment == "" {
packet.Environment = environment
}

outgoingPacket := &outgoingPacket{packet, ch}

// Lazily start background worker until we
// do our first write into the queue.
client.start.Do(func() {
go client.worker()
})

select {
case client.queue <- outgoingPacket:
default:
// Send would block, drop the packet
if client.DropHandler != nil {
client.DropHandler(packet)
}
ch <- ErrPacketDropped
client.wg.Done()
}

return packet.EventID, ch
}

// Capture asynchronously delivers a packet to the Sentry server with the default *Client.
// It is a no-op when client is nil. A channel is provided if it is important to check for a
// send's success.
func Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) {
return DefaultClient.Capture(packet, captureTags)
}

// CaptureMessage formats and delivers a string message to the Sentry server.
func (client *Client) CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string {
if client == nil {
return ""
}

if client.shouldExcludeErr(message) {
return ""
}

packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...)
eventID, _ := client.Capture(packet, tags)

return eventID
}

// CaptureMessage formats and delivers a string message to the Sentry server with the default *Client
func CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string {
return DefaultClient.CaptureMessage(message, tags, interfaces...)
}

// CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
func (client *Client) CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string {
if client == nil {
return ""
}

if client.shouldExcludeErr(message) {
return ""
}

packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...)
eventID, ch := client.Capture(packet, tags)
if eventID != "" {
<-ch
}

return eventID
}

// CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
func CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string {
return DefaultClient.CaptureMessageAndWait(message, tags, interfaces...)
}

// CaptureErrors formats and delivers an error to the Sentry server.
// Adds a stacktrace to the packet, excluding the call to this method.
func (client *Client) CaptureError(err error, tags map[string]string, interfaces ...Interface) string {
if client == nil {
return ""
}

if err == nil {
return ""
}

if client.shouldExcludeErr(err.Error()) {
return ""
}

extra := extractExtra(err)
cause := pkgErrors.Cause(err)

packet := NewPacketWithExtra(err.Error(), extra, append(append(interfaces, client.context.interfaces()...), NewException(cause, GetOrNewStacktrace(cause, 1, 3, client.includePaths)))...)
eventID, _ := client.Capture(packet, tags)

return eventID
}

// CaptureErrors formats and delivers an error to the Sentry server using the default *Client.
// Adds a stacktrace to the packet, excluding the call to this method.
func CaptureError(err error, tags map[string]string, interfaces ...Interface) string {
return DefaultClient.CaptureError(err, tags, interfaces...)
}

// CaptureErrorAndWait is identical to CaptureError, except it blocks and assures that the event was sent
func (client *Client) CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interface) string {
if client == nil {
return ""
}

if client.shouldExcludeErr(err.Error()) {
return ""
}

extra := extractExtra(err)
cause := pkgErrors.Cause(err)

packet := NewPacketWithExtra(err.Error(), extra, append(append(interfaces, client.context.interfaces()...), NewException(cause, GetOrNewStacktrace(cause, 1, 3, client.includePaths)))...)
eventID, ch := client.Capture(packet, tags)
if eventID != "" {
<-ch
}

return eventID
}

// CaptureErrorAndWait is identical to CaptureError, except it blocks and assures that the event was sent
func CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interface) string {
return DefaultClient.CaptureErrorAndWait(err, tags, interfaces...)
}

// CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs.
// If an error is captured, both the error and the reported Sentry error ID are returned.
func (client *Client) CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) {
// Note: This doesn't need to check for client, because we still want to go through the defer/recover path
// Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
// *Packet just to be thrown away, this should not be the normal case. Could be refactored to
// be completely noop though if we cared.
defer func() {
var packet *Packet
err = recover()
switch rval := err.(type) {
case nil:
return
case error:
if client.shouldExcludeErr(rval.Error()) {
return
}
packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...)
default:
rvalStr := fmt.Sprint(rval)
if client.shouldExcludeErr(rvalStr) {
return
}
packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...)
}

errorID, _ = client.Capture(packet, tags)
}()

f()
return
}

// CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs.
// If an error is captured, both the error and the reported Sentry error ID are returned.
func CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) {
return DefaultClient.CapturePanic(f, tags, interfaces...)
}

// CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent
func (client *Client) CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) {
// Note: This doesn't need to check for client, because we still want to go through the defer/recover path
// Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
// *Packet just to be thrown away, this should not be the normal case. Could be refactored to
// be completely noop though if we cared.
defer func() {
var packet *Packet
err = recover()
switch rval := err.(type) {
case nil:
return
case error:
if client.shouldExcludeErr(rval.Error()) {
return
}
packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...)
default:
rvalStr := fmt.Sprint(rval)
if client.shouldExcludeErr(rvalStr) {
return
}
packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...)
}

var ch chan error
errorID, ch = client.Capture(packet, tags)
if errorID != "" {
<-ch
}
}()

f()
return
}

// CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent
func CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) {
return DefaultClient.CapturePanicAndWait(f, tags, interfaces...)
}

func (client *Client) Close() {
close(client.queue)
}

func Close() { DefaultClient.Close() }

// Wait blocks and waits for all events to finish being sent to Sentry server
func (client *Client) Wait() {
client.wg.Wait()
}

// Wait blocks and waits for all events to finish being sent to Sentry server
func Wait() { DefaultClient.Wait() }

func (client *Client) URL() string {
client.mu.RLock()
defer client.mu.RUnlock()

return client.url
}

func URL() string { return DefaultClient.URL() }

func (client *Client) ProjectID() string {
client.mu.RLock()
defer client.mu.RUnlock()

return client.projectID
}

func ProjectID() string { return DefaultClient.ProjectID() }

func (client *Client) Release() string {
client.mu.RLock()
defer client.mu.RUnlock()

return client.release
}

func Release() string { return DefaultClient.Release() }

func IncludePaths() []string { return DefaultClient.IncludePaths() }

func (client *Client) IncludePaths() []string {
client.mu.RLock()
defer client.mu.RUnlock()

return client.includePaths
}

func SetIncludePaths(p []string) { DefaultClient.SetIncludePaths(p) }

func (client *Client) SetIncludePaths(p []string) {
client.mu.Lock()
defer client.mu.Unlock()

client.includePaths = p
}

func (c *Client) SetUserContext(u *User) {
c.mu.Lock()
defer c.mu.Unlock()
c.context.setUser(u)
}

func (c *Client) SetHttpContext(h *Http) {
c.mu.Lock()
defer c.mu.Unlock()
c.context.setHttp(h)
}

func (c *Client) SetTagsContext(t map[string]string) {
c.mu.Lock()
defer c.mu.Unlock()
c.context.setTags(t)
}

func (c *Client) ClearContext() {
c.mu.Lock()
defer c.mu.Unlock()
c.context.clear()
}

func SetUserContext(u *User) { DefaultClient.SetUserContext(u) }
func SetHttpContext(h *Http) { DefaultClient.SetHttpContext(h) }
func SetTagsContext(t map[string]string) { DefaultClient.SetTagsContext(t) }
func ClearContext() { DefaultClient.ClearContext() }

// HTTPTransport is the default transport, delivering packets to Sentry via the
// HTTP API.
type HTTPTransport struct {
*http.Client
}

func (t *HTTPTransport) Send(url, authHeader string, packet *Packet) error {
if url == "" {
return nil
}

body, contentType, err := serializedPacket(packet)
if err != nil {
return fmt.Errorf("error serializing packet: %v", err)
}
req, err := http.NewRequest("POST", url, body)
if err != nil {
return fmt.Errorf("can't create new request: %v", err)
}
req.Header.Set("X-Sentry-Auth", authHeader)
req.Header.Set("User-Agent", userAgent)
req.Header.Set("Content-Type", contentType)
res, err := t.Do(req)
if err != nil {
return err
}
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
if res.StatusCode != 200 {
return fmt.Errorf("raven: got http status %d - x-sentry-error: %s", res.StatusCode, res.Header.Get("X-Sentry-Error"))
}
return nil
}

func serializedPacket(packet *Packet) (io.Reader, string, error) {
packetJSON, err := packet.JSON()
if err != nil {
return nil, "", fmt.Errorf("error marshaling packet %+v to JSON: %v", packet, err)
}

// Only deflate/base64 the packet if it is bigger than 1KB, as there is
// overhead.
if len(packetJSON) > 1000 {
buf := &bytes.Buffer{}
b64 := base64.NewEncoder(base64.StdEncoding, buf)
deflate, _ := zlib.NewWriterLevel(b64, zlib.BestCompression)
deflate.Write(packetJSON)
deflate.Close()
b64.Close()
return buf, "application/octet-stream", nil
}
return bytes.NewReader(packetJSON), "application/json", nil
}

var hostname string

func init() {
hostname, _ = os.Hostname()
}

+ 319
- 0
vendor/github.com/getsentry/raven-go/client_test.go View File

@@ -0,0 +1,319 @@
package raven

import (
"encoding/json"
"reflect"
"testing"
"time"
)

type testInterface struct{}

func (t *testInterface) Class() string { return "sentry.interfaces.Test" }
func (t *testInterface) Culprit() string { return "codez" }

func TestShouldExcludeErr(t *testing.T) {
regexpStrs := []string{"ERR_TIMEOUT", "should.exclude", "(?i)^big$"}

client := &Client{
Transport: newTransport(),
Tags: nil,
context: &context{},
queue: make(chan *outgoingPacket, MaxQueueBuffer),
}

if err := client.SetIgnoreErrors(regexpStrs); err != nil {
t.Fatalf("invalid regexps %v: %v", regexpStrs, err)
}

testCases := []string{
"there was a ERR_TIMEOUT in handlers.go",
"do not log should.exclude at all",
"BIG",
}

for _, tc := range testCases {
if !client.shouldExcludeErr(tc) {
t.Fatalf("failed to exclude err %q with regexps %v", tc, regexpStrs)
}
}
}

func TestPacketJSON(t *testing.T) {
packet := &Packet{
Project: "1",
EventID: "2",
Platform: "linux",
Culprit: "caused_by",
ServerName: "host1",
Release: "721e41770371db95eee98ca2707686226b993eda",
Environment: "production",
Message: "test",
Timestamp: Timestamp(time.Date(2000, 01, 01, 0, 0, 0, 0, time.UTC)),
Level: ERROR,
Logger: "com.getsentry.raven-go.logger-test-packet-json",
Tags: []Tag{Tag{"foo", "bar"}},
Modules: map[string]string{"foo": "bar"},
Fingerprint: []string{"{{ default }}", "a-custom-fingerprint"},
Interfaces: []Interface{&Message{Message: "foo"}},
}

packet.AddTags(map[string]string{"foo": "foo"})
packet.AddTags(map[string]string{"baz": "buzz"})

expected := `{"message":"test","event_id":"2","project":"1","timestamp":"2000-01-01T00:00:00.00","level":"error","logger":"com.getsentry.raven-go.logger-test-packet-json","platform":"linux","culprit":"caused_by","server_name":"host1","release":"721e41770371db95eee98ca2707686226b993eda","environment":"production","tags":[["foo","bar"],["foo","foo"],["baz","buzz"]],"modules":{"foo":"bar"},"fingerprint":["{{ default }}","a-custom-fingerprint"],"logentry":{"message":"foo"}}`
j, err := packet.JSON()
if err != nil {
t.Fatalf("JSON marshalling should not fail: %v", err)
}
actual := string(j)

if actual != expected {
t.Errorf("incorrect json; got %s, want %s", actual, expected)
}
}

func TestPacketJSONNilInterface(t *testing.T) {
packet := &Packet{
Project: "1",
EventID: "2",
Platform: "linux",
Culprit: "caused_by",
ServerName: "host1",
Release: "721e41770371db95eee98ca2707686226b993eda",
Environment: "production",
Message: "test",
Timestamp: Timestamp(time.Date(2000, 01, 01, 0, 0, 0, 0, time.UTC)),
Level: ERROR,
Logger: "com.getsentry.raven-go.logger-test-packet-json",
Tags: []Tag{Tag{"foo", "bar"}},
Modules: map[string]string{"foo": "bar"},
Fingerprint: []string{"{{ default }}", "a-custom-fingerprint"},
Interfaces: []Interface{&Message{Message: "foo"}, nil},
}

expected := `{"message":"test","event_id":"2","project":"1","timestamp":"2000-01-01T00:00:00.00","level":"error","logger":"com.getsentry.raven-go.logger-test-packet-json","platform":"linux","culprit":"caused_by","server_name":"host1","release":"721e41770371db95eee98ca2707686226b993eda","environment":"production","tags":[["foo","bar"]],"modules":{"foo":"bar"},"fingerprint":["{{ default }}","a-custom-fingerprint"],"logentry":{"message":"foo"}}`
j, err := packet.JSON()
if err != nil {
t.Fatalf("JSON marshalling should not fail: %v", err)
}
actual := string(j)

if actual != expected {
t.Errorf("incorrect json; got %s, want %s", actual, expected)
}
}

func TestPacketInit(t *testing.T) {
packet := &Packet{Message: "a", Interfaces: []Interface{&testInterface{}}}
packet.Init("foo")

if packet.Project != "foo" {
t.Error("incorrect Project:", packet.Project)
}
if packet.Culprit != "codez" {
t.Error("incorrect Culprit:", packet.Culprit)
}
if packet.ServerName == "" {
t.Errorf("ServerName should not be empty")
}
if packet.Level != ERROR {
t.Errorf("incorrect Level: got %s, want %s", packet.Level, ERROR)
}
if packet.Logger != "root" {
t.Errorf("incorrect Logger: got %s, want %s", packet.Logger, "root")
}
if time.Time(packet.Timestamp).IsZero() {
t.Error("Timestamp is zero")
}
if len(packet.EventID) != 32 {
t.Error("incorrect EventID:", packet.EventID)
}
}

func TestSetDSN(t *testing.T) {
client := &Client{}
client.SetDSN("https://u:p@example.com/sentry/1")

if client.url != "https://example.com/sentry/api/1/store/" {
t.Error("incorrect url:", client.url)
}
if client.projectID != "1" {
t.Error("incorrect projectID:", client.projectID)
}
if client.authHeader != "Sentry sentry_version=4, sentry_key=u, sentry_secret=p" {
t.Error("incorrect authHeader:", client.authHeader)
}
}

func TestNewClient(t *testing.T) {
client := newClient(nil)
if client.sampleRate != 1.0 {
t.Error("invalid default sample rate")
}
}

func TestSetSampleRate(t *testing.T) {
client := &Client{}
err := client.SetSampleRate(0.2)

if err != nil {
t.Error("invalid sample rate")
}

if client.sampleRate != 0.2 {
t.Error("incorrect sample rate: ", client.sampleRate)
}
}

func TestSetSampleRateInvalid(t *testing.T) {
client := &Client{}
err := client.SetSampleRate(-1.0)

if err != ErrInvalidSampleRate {
t.Error("invalid sample rate should return ErrInvalidSampleRate")
}
}

func TestUnmarshalTag(t *testing.T) {
actual := new(Tag)
if err := json.Unmarshal([]byte(`["foo","bar"]`), actual); err != nil {
t.Fatal("unable to decode JSON:", err)
}

expected := &Tag{Key: "foo", Value: "bar"}
if !reflect.DeepEqual(actual, expected) {
t.Errorf("incorrect Tag: wanted '%+v' and got '%+v'", expected, actual)
}
}

func TestUnmarshalTags(t *testing.T) {
tests := []struct {
Input string
Expected Tags
}{
{
`{"foo":"bar"}`,
Tags{Tag{Key: "foo", Value: "bar"}},
},
{
`[["foo","bar"],["bar","baz"]]`,
Tags{Tag{Key: "foo", Value: "bar"}, Tag{Key: "bar", Value: "baz"}},
},
}

for _, test := range tests {
var actual Tags
if err := json.Unmarshal([]byte(test.Input), &actual); err != nil {
t.Fatal("unable to decode JSON:", err)
}

if !reflect.DeepEqual(actual, test.Expected) {
t.Errorf("incorrect Tags: wanted '%+v' and got '%+v'", test.Expected, actual)
}
}
}

func TestMarshalTimestamp(t *testing.T) {
timestamp := Timestamp(time.Date(2000, 01, 02, 03, 04, 05, 0, time.UTC))
expected := `"2000-01-02T03:04:05.00"`

actual, err := json.Marshal(timestamp)
if err != nil {
t.Error(err)
}

if string(actual) != expected {
t.Errorf("incorrect string; got %s, want %s", actual, expected)
}
}

func TestUnmarshalTimestamp(t *testing.T) {
timestamp := `"2000-01-02T03:04:05.00"`
expected := Timestamp(time.Date(2000, 01, 02, 03, 04, 05, 0, time.UTC))

var actual Timestamp
err := json.Unmarshal([]byte(timestamp), &actual)
if err != nil {
t.Error(err)
}

if actual != expected {
t.Errorf("incorrect string; got %s, want %s", actual.Format("2006-01-02 15:04:05 -0700"), expected.Format("2006-01-02 15:04:05 -0700"))
}
}

func TestNilClient(t *testing.T) {
var client *Client = nil
eventID, ch := client.Capture(nil, nil)
if eventID != "" {
t.Error("expected empty eventID:", eventID)
}
// wait on ch: no send should succeed immediately
err := <-ch
if err != nil {
t.Error("expected nil err:", err)
}
}

func TestCaptureNil(t *testing.T) {
var client *Client = DefaultClient
eventID, ch := client.Capture(nil, nil)
if eventID != "" {
t.Error("expected empty eventID:", eventID)
}
// wait on ch: no send should succeed immediately
err := <-ch
if err != nil {
t.Error("expected nil err:", err)
}
}

func TestCaptureNilError(t *testing.T) {
var client *Client = DefaultClient
eventID := client.CaptureError(nil, nil)
if eventID != "" {
t.Error("expected empty eventID:", eventID)
}
}

func TestNewPacketWithExtraSetsDefault(t *testing.T) {
testCases := []struct {
Extra Extra
Expected Extra
}{
// Defaults should be set when nil is passed
{
Extra: nil,
Expected: setExtraDefaults(Extra{}),
},
// Defaults should be set when empty is passed
{
Extra: Extra{},
Expected: setExtraDefaults(Extra{}),
},
// Packet should always override default keys
{
Extra: Extra{
"runtime.Version": "notagoversion",
},
Expected: setExtraDefaults(Extra{}),
},
// Packet should include our extra info
{
Extra: Extra{
"extra.extra": "extra",
},
Expected: setExtraDefaults(Extra{
"extra.extra": "extra",
}),
},
}

for i, test := range testCases {
packet := NewPacketWithExtra("packet", test.Extra)
if !reflect.DeepEqual(packet.Extra, test.Expected) {
t.Errorf("Case [%d]: Expected packet: %+v, got: %+v", i, test.Expected, packet.Extra)
}
}
}

+ 60
- 0
vendor/github.com/getsentry/raven-go/errors.go View File

@@ -0,0 +1,60 @@
package raven

type causer interface {
Cause() error
}

type errWrappedWithExtra struct {
err error
extraInfo map[string]interface{}
}

func (ewx *errWrappedWithExtra) Error() string {
return ewx.err.Error()
}

func (ewx *errWrappedWithExtra) Cause() error {