Skip to content

Commit

Permalink
feat: ask user for email on first run of Kurtosis (#1001)
Browse files Browse the repository at this point in the history
  • Loading branch information
h4ck3rk3y committed Jul 27, 2023
1 parent 768e95d commit 0f33b5b
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 81 deletions.
5 changes: 3 additions & 2 deletions cli/cli/go.mod
Expand Up @@ -23,7 +23,6 @@ require (
github.com/kurtosis-tech/kurtosis/contexts-config-store v0.0.0 // local dependency
github.com/kurtosis-tech/kurtosis/engine/launcher v0.0.0 // local dependency
github.com/kurtosis-tech/kurtosis/kurtosis_version v0.0.0 // Local dependency generated during build
github.com/kurtosis-tech/metrics-library/golang v0.0.0-20230427161256-0c1550da27b5
github.com/kurtosis-tech/stacktrace v0.0.0-20211028211901-1c67a77b5409
github.com/manifoldco/promptui v0.9.0
github.com/mattn/go-isatty v0.0.19
Expand All @@ -46,7 +45,10 @@ require (
github.com/fatih/color v1.13.0
github.com/google/go-github/v50 v50.2.0
github.com/google/uuid v1.3.0
github.com/joho/godotenv v1.5.1
github.com/kurtosis-tech/kurtosis-portal/api/golang v0.0.0-20230712110324-ce92904bb514
github.com/kurtosis-tech/kurtosis/name_generator v0.0.0-20230727131617-20b635a2fc7e
github.com/kurtosis-tech/metrics-library/golang v0.0.0-20230727131823-40788a849ce5
github.com/kurtosis-tech/minimal-grpc-server/golang v0.0.0-20230710164206-90b674acb269
github.com/kurtosis-tech/vscode-kurtosis/starlark-lsp v0.0.0-20230406131103-c466e04f1b89
github.com/mholt/archiver v3.1.1+incompatible
Expand Down Expand Up @@ -93,7 +95,6 @@ require (
github.com/imdario/mergo v0.3.16 // indirect
github.com/improbable-eng/grpc-web v0.15.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.7 // indirect
Expand Down
9 changes: 4 additions & 5 deletions cli/cli/go.sum
Expand Up @@ -261,7 +261,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
Expand Down Expand Up @@ -359,8 +358,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kurtosis-tech/kurtosis-portal/api/golang v0.0.0-20230712110324-ce92904bb514 h1:FBM2wGL9BTzj+qVs+Jd8l1mYAGd2uaKJdL7Ccei2DGU=
github.com/kurtosis-tech/kurtosis-portal/api/golang v0.0.0-20230712110324-ce92904bb514/go.mod h1:pHSIQlUtd+zPdILuvu3uCydjypzJk/wLex9bePmuqWA=
github.com/kurtosis-tech/metrics-library/golang v0.0.0-20230427161256-0c1550da27b5 h1:Ptol0ZtFcecI9jjabV7OA7SWc8SSQlMo+FpozqCehWU=
github.com/kurtosis-tech/metrics-library/golang v0.0.0-20230427161256-0c1550da27b5/go.mod h1:tteWV+M47xMHxqCIPQmdmgPW80rhN8YfzrgRRWbQhOw=
github.com/kurtosis-tech/kurtosis/name_generator v0.0.0-20230727131617-20b635a2fc7e h1:edrARHJ2xOR8kcE+nW32iJMGtImwzJqTkjcUHTbovmk=
github.com/kurtosis-tech/kurtosis/name_generator v0.0.0-20230727131617-20b635a2fc7e/go.mod h1:BReV/l+0pvK7K9wf8MN41ViQBSQH30j+YJ7V4glf19A=
github.com/kurtosis-tech/metrics-library/golang v0.0.0-20230727131823-40788a849ce5 h1:OVnlFVT5ooscDchTKMK6o46/s25XlBDEliSZqp63FfY=
github.com/kurtosis-tech/metrics-library/golang v0.0.0-20230727131823-40788a849ce5/go.mod h1:tteWV+M47xMHxqCIPQmdmgPW80rhN8YfzrgRRWbQhOw=
github.com/kurtosis-tech/minimal-grpc-server/golang v0.0.0-20230710164206-90b674acb269 h1:yOo1I1iAyp0oYcGJ8AEAvt95QmpKNL1NYm1ZDqJW/LU=
github.com/kurtosis-tech/minimal-grpc-server/golang v0.0.0-20230710164206-90b674acb269/go.mod h1:hCgrTsWf5Z8i+DOIvOw5xBMnNjrBruv89s1hjtbAPcw=
github.com/kurtosis-tech/stacktrace v0.0.0-20211028211901-1c67a77b5409 h1:YQTATifMUwZEtZYb0LVA7DK2pj8s71iY8rzweuUQ5+g=
Expand Down Expand Up @@ -647,7 +648,6 @@ go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.starlark.net v0.0.0-20210223155950-e043a3d3c984/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
Expand Down Expand Up @@ -845,7 +845,6 @@ google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
Expand Down
@@ -1,11 +1,11 @@
package metrics_client_factory
package do_nothing_metrics_client_callback

// this call back does nothing
// the metrics client allows us to specify a call back that does things after a successful/failure
// we use it in the install-consent to clear the backlog & associated file; but we don't use it for other metrics
type doNothingMetricsClientCallback struct{}

func newDoNothingMetricsClientCallback() doNothingMetricsClientCallback {
func NewDoNothingMetricsClientCallback() doNothingMetricsClientCallback {
return doNothingMetricsClientCallback{}
}

Expand Down
Expand Up @@ -2,6 +2,7 @@ package metrics_client_factory

import (
"github.com/kurtosis-tech/kurtosis/cli/cli/defaults"
"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/do_nothing_metrics_client_callback"
"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/logrus_logger_converter"
"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/metrics_user_id_store"
"github.com/kurtosis-tech/kurtosis/cli/cli/kurtosis_cluster_setting"
Expand Down Expand Up @@ -63,7 +64,7 @@ func GetMetricsClient() (metrics_client.MetricsClient, func() error, error) {
clusterType,
sendUserMetrics,
shouldFlushMetricsClientQueueOnEachEvent,
newDoNothingMetricsClientCallback(),
do_nothing_metrics_client_callback.NewDoNothingMetricsClientCallback(),
logrus_logger_converter.ConvertLogrusLoggerToAnalyticsLogger(logger),
)

Expand Down
82 changes: 11 additions & 71 deletions cli/cli/helpers/prompt_displayer/prompt_displayer.go
@@ -1,51 +1,24 @@
package prompt_displayer

import (
"fmt"
"github.com/kurtosis-tech/stacktrace"
"github.com/manifoldco/promptui"
"github.com/sirupsen/logrus"
"strings"
"net/mail"
)

const (
//Valid confirm inputs
yInput validPromptInput = "y"
yesInput validPromptInput = "yes"

//Valid not confirm inputs
nInput validPromptInput = "n"
noInput validPromptInput = "no"

// Anything beyond this and it runs off the edge of the screen so the user doesn't see it
maxLabelLength = 110
)

type validPromptInput string

var validConfirmInputs = []validPromptInput{yInput, yesInput}
var validRejectInputs = []validPromptInput{nInput, noInput}
var allValidDecisionInputs = append(validConfirmInputs, validRejectInputs...)

func DisplayConfirmationPromptAndGetBooleanResult(label string, defaultValue bool) (bool, error) {
func DisplayConfirmationPromptAndGetBooleanResult(label string, defaultValue string) (string, error) {
if len(label) > maxLabelLength {
return false, stacktrace.NewError("Label '%v' is longer than the maximum allowed characters, '%v'", label, maxLabelLength)
return "", stacktrace.NewError("Label '%v' is longer than the maximum allowed characters, '%v'", label, maxLabelLength)
}

defaultValueStr := string(nInput)
if defaultValue {
defaultValueStr = string(yInput)
}

labelWithValidInputs := fmt.Sprintf(
"%v (%v/%v)",
label,
yInput,
nInput,
)
prompt := promptui.Prompt{
Label: labelWithValidInputs,
Default: defaultValueStr,
Label: label,
Default: defaultValue,
AllowEdit: false,
Validate: validateConfirmationInput,
Mask: 0,
Expand All @@ -60,13 +33,11 @@ func DisplayConfirmationPromptAndGetBooleanResult(label string, defaultValue boo

userInput, err := prompt.Run()
if err != nil {
return false, stacktrace.Propagate(err, "An error occurred displaying the prompt")
return "", stacktrace.Propagate(err, "An error occurred displaying the prompt")
}
logrus.Debugf("User input: '%v'", userInput)

didUserConfirm := isConfirmationInput(userInput)

return didUserConfirm, nil
return userInput, nil
}

// ====================================================================================================
Expand All @@ -75,40 +46,9 @@ func DisplayConfirmationPromptAndGetBooleanResult(label string, defaultValue boo
//
// ====================================================================================================
func validateConfirmationInput(input string) error {
isValid := contains(allValidDecisionInputs, input)
if !isValid {
return stacktrace.NewError(
"You have entered an invalid input '%v'. "+
"Valid inputs for confirmation: '%v' "+
"Valid inputs for rejection: '%v'",
input,
getValidInputsListStrFromValidPromptInputsSlice(validConfirmInputs),
getValidInputsListStrFromValidPromptInputsSlice(validRejectInputs))
}

return nil
}

func isConfirmationInput(input string) bool {
return contains(validConfirmInputs, input)
}

func contains(s []validPromptInput, str string) bool {
for _, v := range s {
vStr := string(v)
if strings.EqualFold(vStr, str) {
return true
}
}
return false
}

func getValidInputsListStrFromValidPromptInputsSlice(validUserInputsSlice []validPromptInput) string {
var validInputsSliceStr []string

for _, validInput := range validUserInputsSlice {
validInputsSliceStr = append(validInputsSliceStr, string(validInput))
if input == "" {
return nil
}
validInputsListStr := strings.Join(validInputsSliceStr, `','`)
return validInputsListStr
_, parsingErr := mail.ParseAddress(input)
return parsingErr
}
70 changes: 70 additions & 0 deletions cli/cli/kurtosis_config/email_collector/email_collector.go
@@ -0,0 +1,70 @@
package email_collector

import (
"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/do_nothing_metrics_client_callback"
"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/logrus_logger_converter"
"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/metrics_user_id_store"
"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/prompt_displayer"
"github.com/kurtosis-tech/kurtosis/cli/cli/kurtosis_config/resolved_config"
"github.com/kurtosis-tech/kurtosis/kurtosis_version"
metrics_client "github.com/kurtosis-tech/metrics-library/golang/lib/client"
"github.com/kurtosis-tech/metrics-library/golang/lib/source"
"github.com/sirupsen/logrus"
)

const (
defaultEmailValue = ""
emailValueInputPrompt = "(Optional) Share your email address for occasional updates & outreach for product feedback from Kurtosis"
sendUserMetrics = true
flushQueueOnEachEvent = false
)

func AskUserForEmailAndLogIt() {
userEmail, err := prompt_displayer.DisplayConfirmationPromptAndGetBooleanResult(emailValueInputPrompt, defaultEmailValue)
if err != nil {
logrus.Debugf("The user tried to input his email address but it failed")
}

if userEmail != defaultEmailValue {
logUserEmailAddressAsMetric(userEmail)
}

}

// TODO this recreates a metrics client instead of using the factory as there are circular dependencies - clean this up
func logUserEmailAddressAsMetric(userEmail string) {
metricsUserIdStore := metrics_user_id_store.GetMetricsUserIDStore()
metricsUserId, err := metricsUserIdStore.GetUserID()
if err != nil {
logrus.Debugf("an error occurred while getting users metrics id:\n%v", err)
return
}
logger := logrus.StandardLogger()

metricsClient, metricsClientCloseFunc, err := metrics_client.CreateMetricsClient(
source.KurtosisCLISource,
kurtosis_version.KurtosisVersion,
metricsUserId,
// TODO this isn't relevant for the metric also this only runs at first install;
// The user hasn't ever used Kurtosis yet so it has to be the default cluster
resolved_config.DefaultDockerClusterName,
sendUserMetrics,
flushQueueOnEachEvent,
do_nothing_metrics_client_callback.NewDoNothingMetricsClientCallback(),
logrus_logger_converter.ConvertLogrusLoggerToAnalyticsLogger(logger),
)
if err != nil {
logrus.Debugf("tried creating a metrics client but failed with error:\n%v", err)
return
}
defer func() {
err = metricsClientCloseFunc()
if err != nil {
logrus.Debugf("an error occurred while closing the metrics client:\n%v", err)
}
}()
if err = metricsClient.TrackUserSharedEmailAddress(userEmail); err != nil {
logrus.Debugf("tried sending user email address as metric but failed:\n%v", err)
return
}
}
2 changes: 2 additions & 0 deletions cli/cli/kurtosis_config/kurtosis_config_provider.go
@@ -1,6 +1,7 @@
package kurtosis_config

import (
"github.com/kurtosis-tech/kurtosis/cli/cli/kurtosis_config/email_collector"
"github.com/kurtosis-tech/kurtosis/cli/cli/kurtosis_config/resolved_config"
"github.com/kurtosis-tech/stacktrace"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -35,6 +36,7 @@ func (configProvider *KurtosisConfigProvider) GetOrInitializeConfig() (*resolved
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred executing init interactive config")
}
email_collector.AskUserForEmailAndLogIt()

if err = configProvider.configStore.SetConfig(kurtosisConfig); err != nil {
return nil, stacktrace.Propagate(err, "An error occurred setting Kurtosis config")
Expand Down

0 comments on commit 0f33b5b

Please sign in to comment.