Skip to content

Commit

Permalink
feat: introduce usage analytics (#379)
Browse files Browse the repository at this point in the history
Analytics were temporarily removed as we sent them to a third-party
server.
With this new implementation, these are being sent to Kong Inc directly.

Furthermore, to alleviate concerns raised
in #242, we are now providing a flag on
the root-level, in addition to DECK_ANALYTICS environment variable to
tweak the setting.
  • Loading branch information
hbagdi committed May 13, 2021
1 parent 7b3d303 commit a233393
Show file tree
Hide file tree
Showing 13 changed files with 143 additions and 10 deletions.
40 changes: 31 additions & 9 deletions cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,23 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
}

// load Kong version after workspace
kongVersion, err := kongVersion(ctx, wsConfig)
kongVersion, err := fetchKongVersion(ctx, wsConfig)
if err != nil {
return errors.Wrap(err, "reading Kong version")
}
parsedKongVersion, err := parseKongVersion(kongVersion)
if err != nil {
return errors.Wrap(err, "parsing Kong version")
}

// TODO: instead of guessing the cobra command here, move the sendAnalytics
// call to the RunE function. That is not trivial because it requires the
// workspace name and kong client to be present on that level.
cmd := "sync"
if dry {
cmd = "diff"
}
_ = sendAnalytics(cmd, kongVersion)

workspaceExists, err := workspaceExists(ctx, wsConfig)
if err != nil {
Expand Down Expand Up @@ -135,7 +148,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
// read the target state
rawState, err := file.Get(targetContent, file.RenderConfig{
CurrentState: currentState,
KongVersion: kongVersion,
KongVersion: parsedKongVersion,
})
if err != nil {
return err
Expand Down Expand Up @@ -166,8 +179,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
return nil
}

func kongVersion(ctx context.Context,
config utils.KongClientConfig) (semver.Version, error) {
func fetchKongVersion(ctx context.Context, config utils.KongClientConfig) (string, error) {

var version string

Expand All @@ -177,30 +189,33 @@ func kongVersion(ctx context.Context,
config.Workspace = ""
client, err := utils.GetKongClient(config)
if err != nil {
return semver.Version{}, err
return "", err
}
root, err := client.Root(ctx)
if err != nil {
if workspace == "" {
return semver.Version{}, err
return "", err
}
// try with workspace path
req, err := http.NewRequest("GET",
utils.CleanAddress(config.Address)+"/"+workspace+"/kong",
nil)
if err != nil {
return semver.Version{}, err
return "", err
}
var resp map[string]interface{}
_, err = client.Do(nil, req, &resp)
_, err = client.Do(ctx, req, &resp)
if err != nil {
return semver.Version{}, err
return "", err
}
version = resp["version"].(string)
} else {
version = root["version"].(string)
}
return version, nil
}

func parseKongVersion(version string) (semver.Version, error) {
v, err := utils.CleanKongVersion(version)
if err != nil {
return semver.Version{}, err
Expand Down Expand Up @@ -246,3 +261,10 @@ func containsProxyConfiguration(content utils.KongRawState) bool {
func containsRBACConfiguration(content utils.KongRawState) bool {
return len(content.RBACRoles) != 0
}

func sendAnalytics(cmd, kongVersion string) error {
if disableAnalytics {
return nil
}
return utils.SendAnalytics(cmd, VERSION, kongVersion)
}
6 changes: 6 additions & 0 deletions cmd/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ configure Kong.`,

format := file.Format(strings.ToUpper(dumpCmdStateFormat))

kongVersion, err := fetchKongVersion(ctx, rootConfig.ForWorkspace(dumpWorkspace))
if err != nil {
return errors.Wrap(err, "reading Kong version")
}
_ = sendAnalytics("dump", kongVersion)

// Kong Enterprise dump all workspace
if dumpAllWorkspaces {
if dumpWorkspace != "" {
Expand Down
1 change: 1 addition & 0 deletions cmd/konnect_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ the entities present in files locally. This allows you to see the entities
that will be created or updated or deleted.` + konnectAlphaState,
Args: validateNoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
_ = sendAnalytics("konnect-diff", "")
return syncKonnect(cmd.Context(), konnectDiffCmdKongStateFile, true,
konnectDiffCmdParallelism)
},
Expand Down
1 change: 1 addition & 0 deletions cmd/konnect_dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ configure Konnect.` + konnectAlphaState,
Args: validateNoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
httpClient := utils.HTTPClient()
_ = sendAnalytics("konnect-dump", "")

if yes, err := utils.ConfirmFileOverwrite(konnectDumpCmdKongStateFile, dumpCmdStateFormat, assumeYes); err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions cmd/konnect_ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ can connect to Konnect's API endpoint. It also validates the supplied
credentials.` + konnectAlphaState,
Args: validateNoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
_ = utils.SendAnalytics("konnect-ping", VERSION, "")
client, err := utils.GetKonnectClient(nil, konnectConfig.Debug)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions cmd/konnect_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var konnectSyncCmd = &cobra.Command{
to get Konnect's state in sync with the input state.` + konnectAlphaState,
Args: validateNoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
_ = sendAnalytics("konnect-sync", "")
return syncKonnect(cmd.Context(), konnectDiffCmdKongStateFile, false,
konnectDiffCmdParallelism)
},
Expand Down
3 changes: 2 additions & 1 deletion cmd/ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ can connect to Kong's Admin API or not.`,
ctx := cmd.Context()

wsConfig := rootConfig.ForWorkspace(pingWorkspace)
version, err := kongVersion(ctx, wsConfig)
version, err := fetchKongVersion(ctx, wsConfig)
if err != nil {
return errors.Wrap(err, "reading Kong version")
}
_ = sendAnalytics("ping", version)
fmt.Println("Successfully connected to Kong!")
fmt.Println("Kong version: ", version)
return nil
Expand Down
7 changes: 7 additions & 0 deletions cmd/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ By default, this command will ask for a confirmation prompt.`,
if err != nil {
return err
}

kongVersion, err := fetchKongVersion(ctx, rootConfig.ForWorkspace(resetWorkspace))
if err != nil {
return errors.Wrap(err, "reading Kong version")
}
_ = sendAnalytics("reset", kongVersion)

// Kong OSS or default workspace
if !resetAllWorkspaces && resetWorkspace == "" {
state, err := dump.Get(ctx, rootClient, dumpConfig)
Expand Down
8 changes: 8 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ var (
cfgFile string
rootConfig utils.KongClientConfig
konnectConfig utils.KonnectConfig

disableAnalytics bool
)

// rootCmd represents the base command when called without any subcommands
Expand Down Expand Up @@ -126,6 +128,11 @@ func init() {
"File containing password to your Konnect account")
viper.BindPFlag("konnect-password-file",
rootCmd.PersistentFlags().Lookup("konnect-password-file"))

rootCmd.PersistentFlags().Bool("analytics", true,
"share anonymized data to help improve decK")
viper.BindPFlag("analytics",
rootCmd.PersistentFlags().Lookup("analytics"))
}

// initConfig reads in config file and ENV variables if set.
Expand Down Expand Up @@ -189,6 +196,7 @@ func initKonnectConfig() error {
password = strings.TrimRight(password, "\n")
}

disableAnalytics = !viper.GetBool("analytics")
konnectConfig.Email = viper.GetString("konnect-email")
konnectConfig.Password = password
konnectConfig.Debug = (viper.GetInt("verbose") >= 1)
Expand Down
1 change: 1 addition & 0 deletions cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ this command.
`,
Args: validateNoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
_ = sendAnalytics("validate", "")
// read target file
// this does json schema validation as well
targetContent, err := file.GetContentFromFiles(validateCmdKongStateFile)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/pelletier/go-toml v1.7.0 // indirect
github.com/pkg/errors v0.9.1
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shirou/gopsutil/v3 v3.21.3
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v0.0.7
Expand Down
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/jsonschema v0.0.0-20191017121752-4bb6e3fae4f2 h1:swGeCLPiUQ647AIRnFxnAHdzlg6IPpmU6QdkOPZINt8=
github.com/alecthomas/jsonschema v0.0.0-20191017121752-4bb6e3fae4f2/go.mod h1:Juc2PrI3wtNfUwptSvAIeNx+HrETwHQs6nf+TkOJlOA=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
Expand Down Expand Up @@ -76,6 +78,8 @@ github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
Expand Down Expand Up @@ -266,6 +270,8 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil/v3 v3.21.3 h1:wgcdAHZS2H6qy4JFewVTtqfiYxFzCeEJod/mLztdPG8=
github.com/shirou/gopsutil/v3 v3.21.3/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
Expand Down Expand Up @@ -304,6 +310,10 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M=
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=
github.com/tklauser/numcpus v0.2.1 h1:ct88eFm+Q7m2ZfXJdan1xYoXKlmwsfP+k88q05KvlZc=
github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
Expand Down Expand Up @@ -415,6 +425,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand Down
72 changes: 72 additions & 0 deletions utils/analytics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package utils

import (
"bytes"
"fmt"
"net"
"os"
"runtime"
"strings"

"github.com/shirou/gopsutil/v3/host"
)

const (
reportsHost = "kong-hf.konghq.com"
reportsPort = 61829
)

func SendAnalytics(cmd, deckVersion, kongVersion string) error {
if strings.ToLower(os.Getenv("DECK_ANALYTICS")) == "off" {
return nil
}
if cmd == "" {
return fmt.Errorf("invalid argument, 'cmd' cannot be empty")
}

stats := collectStats(cmd, deckVersion, kongVersion)
body := formatStats(stats)

addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", reportsHost, reportsPort))
if err != nil {
return err
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
return err
}
defer conn.Close()

_, err = conn.Write([]byte(body))
if err != nil {
return err
}
return nil
}

func formatStats(stats map[string]string) string {
var buffer bytes.Buffer
buffer.WriteString("<14>")
for k, v := range stats {
buffer.WriteString(fmt.Sprintf("%s=%s;", k, v))
}
return buffer.String()
}

func collectStats(cmd, deckVersion, kongVersion string) map[string]string {
result := map[string]string{
"signal": "decK",
"v": deckVersion,
"cmd": cmd,
"os": runtime.GOOS,
"arch": runtime.GOARCH,
}
if kongVersion != "" {
result["kv"] = kongVersion
}
info, err := host.Info()
if err == nil {
result["osv"] = info.Platform + " " + info.PlatformVersion
}
return result
}

0 comments on commit a233393

Please sign in to comment.