From 8f47bfc14306d27b88cc1ac5a7d06cdd550b8252 Mon Sep 17 00:00:00 2001 From: constient-altrova Date: Wed, 12 Jun 2024 04:50:11 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=84=20synced=20local=20'internal/'?= =?UTF-8?q?=20with=20remote=20'internal/'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/prompter/tea_onboarding_form.go | 27 ++++++++++-------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/internal/prompter/tea_onboarding_form.go b/internal/prompter/tea_onboarding_form.go index 9ac3a66..af5ea45 100644 --- a/internal/prompter/tea_onboarding_form.go +++ b/internal/prompter/tea_onboarding_form.go @@ -281,7 +281,8 @@ func waitForLog(m *model) { go grpcutil.GetLog(m.config, m.config.Get().Token, m.config.Get().EndPoint, m.config.Get().TeamId, m.config.Get().AccountId, m.sourceId, m.sourceToken, stop) err := <-stop if err != nil { - m.err = errors.New("we apologize for the inconvenience. There seems to be an error on our end or with our server.\nPlease try again later or contact our support team for assistance") + m.err = err + // m.err = errors.New("we apologize for the inconvenience. There seems to be an error on our end or with our server.\nPlease try again later or contact our support team for assistance") m.nextInput() } subStep = "awesome" @@ -294,25 +295,24 @@ func (m *model) handleKeyPres() (tea.Model, tea.Cmd) { switch subStep { case "email": if m.inputs[email].Value() != "" { - msg, err := APICalls.SignupFlow(m.inputs[email].Value(), m.config.Get().EndPoint) + _, err := APICalls.SignupFlow(m.inputs[email].Value(), m.config.Get().EndPoint) if err != nil { m.err = err return m, nil - } else if msg == "already registered user. Sent link to login" { - // m.err = errors.New("you are already a user, please use logfire commands") - // os.Exit(0) - // return m, nil } subStep = "token" - m.nextInput() + m.nextInput() } case "token": if m.inputs[token].Value() != "" { - err := APICalls.TokenSignIn(m.config, m.inputs[token].Value(), m.config.Get().EndPoint) + err, onboarded := APICalls.TokenSignIn(m.config, m.inputs[token].Value(), m.config.Get().EndPoint) if err != nil { m.err = err return m, nil + } else if onboarded { + m.err = errors.New("you have already onboarded. Please use `logfire stream` to start streaming logs or `logfire` to interact with the cli and create teams, sources and more") + return m, nil } step = "account-setup" subStep = "firstName" @@ -617,10 +617,7 @@ func (m model) View() string { currentTime := time.Now() formattedTime := currentTime.Format("2006-01-02 15:04:05") - fmt.Println("Thank you for signing up") - - fmt.Printf( - ` + curlCmd := fmt.Sprintf(` %s %s %s %s \ %s %s \ @@ -628,7 +625,7 @@ func (m model) View() string { %s %s %s -`, + `, continueStyle.Render("******************************************************************************************"), colorThree.Render(`curl`), colorThree.Render(`--location`), @@ -642,9 +639,7 @@ func (m model) View() string { colorTwo.Render("\nOpen Web app or run `logfire stream` to start streaming logs, You can test the ingestion by copying the command and pasting it in any terminal"), ) - os.Exit(0) - - return "Completed!" + return renderWelcome() + renderSection("Signup", true) + m.renderEmail() + m.renderToken() + renderSection("Account setup", true) + m.renderAccountSetup() + renderSection("Create a Team", true) + m.renderTeamName() + renderSection("Send logs", true) + m.renderSource() + m.renderCurlCommand() + awesomeLogReceived + renderSection("Config source", false) + m.renderConfig() + finishMessage + curlCmd + "Completed!" } return "" From 42cc35ad8834dd763b7794f1d0028ffbedea0993 Mon Sep 17 00:00:00 2001 From: constient-altrova Date: Wed, 12 Jun 2024 04:50:11 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=84=20synced=20local=20'pkg/'=20wi?= =?UTF-8?q?th=20remote=20'pkg/'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/cmd/alerts/alerts.go | 8 ++- pkg/cmd/integrations/integrations.go | 5 +- pkg/cmd/login/login.go | 7 ++- pkg/cmd/root/root.go | 7 ++- pkg/cmd/settings/settings.go | 7 ++- pkg/cmd/sources/sources.go | 5 +- pkg/cmd/teams/teams.go | 8 ++- pkg/cmd/views/views.go | 8 ++- pkg/cmdutil/APICalls/profile_api_calls.go | 22 +++++---- pkg/cmdutil/grpcutil/grpc.go | 49 ++++++++++++++----- .../grpcutil/onboarding_wait_for_log.go | 24 ++++----- 11 files changed, 105 insertions(+), 45 deletions(-) diff --git a/pkg/cmd/alerts/alerts.go b/pkg/cmd/alerts/alerts.go index e9d6fa5..bffe1b3 100644 --- a/pkg/cmd/alerts/alerts.go +++ b/pkg/cmd/alerts/alerts.go @@ -3,6 +3,9 @@ package alerts import ( "errors" "fmt" + "net/http" + "os" + "github.com/logfire-sh/cli/internal/config" "github.com/logfire-sh/cli/internal/prompter" "github.com/logfire-sh/cli/pkg/cmd/alerts/alerts_create" @@ -13,7 +16,6 @@ import ( "github.com/logfire-sh/cli/pkg/cmdutil" "github.com/logfire-sh/cli/pkg/iostreams" "github.com/spf13/cobra" - "net/http" ) type PromptAlertOptions struct { @@ -27,7 +29,7 @@ type PromptAlertOptions struct { Choice string } -var choices = []string{"Create", "List", "Delete", "Pause", "Update"} +var choices = []string{"Create", "List", "Delete", "Pause", "Update", "Exit"} func NewCmdAlerts(f *cmdutil.Factory) *cobra.Command { opts := &PromptAlertOptions{ @@ -60,6 +62,8 @@ func NewCmdAlerts(f *cmdutil.Factory) *cobra.Command { alerts_pause.NewPauseAlertCmd(f).Run(cmd, []string{}) case choices[4]: alerts_update.NewAlertUpdateCmd(f).Run(cmd, []string{}) + case "Exit": + os.Exit(0) } }, } diff --git a/pkg/cmd/integrations/integrations.go b/pkg/cmd/integrations/integrations.go index 0ed7235..bcde0e1 100644 --- a/pkg/cmd/integrations/integrations.go +++ b/pkg/cmd/integrations/integrations.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/http" + "os" "github.com/logfire-sh/cli/internal/config" "github.com/logfire-sh/cli/internal/prompter" @@ -27,7 +28,7 @@ type PromptIntegrationsOptions struct { Choice string } -var choices = []string{"Create", "List", "Delete", "Update"} +var choices = []string{"Create", "List", "Delete", "Update", "Exit"} func NewCmdIntegrations(f *cmdutil.Factory) *cobra.Command { opts := &PromptIntegrationsOptions{ @@ -58,6 +59,8 @@ func NewCmdIntegrations(f *cmdutil.Factory) *cobra.Command { integrations_delete.NewDeleteIntegrationCmd(f).Run(cmd, []string{}) case choices[3]: integrations_update.NewUpdateIntegrationsCmd(f).Run(cmd, []string{}) + case "Exit": + os.Exit(0) } }, } diff --git a/pkg/cmd/login/login.go b/pkg/cmd/login/login.go index 1cd4c81..8e844b2 100644 --- a/pkg/cmd/login/login.go +++ b/pkg/cmd/login/login.go @@ -66,7 +66,7 @@ func NewLoginCmd(f *cmdutil.Factory) *cobra.Command { # authenticate against logfire.ai by magic link token # First request a Magic link to your email address $ logfire login --email - + # Second authenticate using the token received on your email address $ logfire login --token `), @@ -120,7 +120,7 @@ func loginRun(opts *LoginOptions) { } } - var choiceList = []string{"Magic link", "Password"} + var choiceList = []string{"Magic link", "Password", "Exit"} if opts.Interactive && opts.Token == "" && opts.Email == "" && opts.Password == "" { choice, err := opts.Prompter.Select("Select login method (Default: Magic link)", "Magic link", choiceList) @@ -187,6 +187,9 @@ func loginRun(opts *LoginOptions) { fmt.Fprintf(opts.IO.ErrOut, "\n%s Failed to sign in\n", cs.FailureIcon()) return } + + case "Exit": + os.Exit(0) } } else { isEmpty := func(s string) bool { diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index abf0bc6..88960d8 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -2,6 +2,7 @@ package root import ( "fmt" + "os" "github.com/logfire-sh/cli/pkg/cmd/settings" @@ -40,7 +41,7 @@ type PromptRootOptions struct { } var choices = []string{"Reset password", "Logout", "Sources", "Teams", - "Tail", "Start Stream", "Views", "Alerts", "Integrations", "SQL", "Update profile", "Settings", "Round trip"} + "Tail", "Start Stream", "Views", "Alerts", "Integrations", "SQL", "Update profile", "Settings", "Round trip", "Exit"} var NotLoggedInChoices = []string{"Signup", "Login"} @@ -63,7 +64,7 @@ func NewCmdRoot(f *cmdutil.Factory, cmdCh chan bool) (*cobra.Command, error) { Example: heredoc.Doc(` $ logfire login $ logfire stream livetail - + `), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { // require that the user is authenticated before running most commands @@ -131,6 +132,8 @@ func NewCmdRoot(f *cmdutil.Factory, cmdCh chan bool) (*cobra.Command, error) { settings.SettingsCmd(f).Run(cmd, []string{}) case choices[12]: roundtrip.NewCmdRoundTrip(f).Run(cmd, []string{}) + case "Exit": + os.Exit(0) default: break } diff --git a/pkg/cmd/settings/settings.go b/pkg/cmd/settings/settings.go index a3ec67c..d938998 100644 --- a/pkg/cmd/settings/settings.go +++ b/pkg/cmd/settings/settings.go @@ -3,6 +3,7 @@ package settings import ( "fmt" "net/http" + "os" "strings" "github.com/MakeNowJust/heredoc" @@ -16,7 +17,7 @@ import ( "github.com/spf13/cobra" ) -var choices = []string{"Change default team", "Change theme"} +var choices = []string{"Change default team", "Change theme", "Exit"} type SettingsOptions struct { IO *iostreams.IOStreams @@ -95,6 +96,10 @@ func SettingsRun(opts *SettingsOptions) { } } + if opts.Interactive && opts.Choice == "Exit" { + os.Exit(0) + } + if opts.Interactive && opts.Choice == "Change theme" { themeOptions := []string{"Dark", "Light"} diff --git a/pkg/cmd/sources/sources.go b/pkg/cmd/sources/sources.go index 332b603..8467f78 100644 --- a/pkg/cmd/sources/sources.go +++ b/pkg/cmd/sources/sources.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/http" + "os" "github.com/logfire-sh/cli/internal/config" "github.com/logfire-sh/cli/internal/prompter" @@ -28,7 +29,7 @@ type PromptSourceOptions struct { Choice string } -var choices = []string{"Create", "List", "Delete", "Update", "Configuration"} +var choices = []string{"Create", "List", "Delete", "Update", "Configuration", "Exit"} func NewCmdSource(f *cmdutil.Factory) *cobra.Command { opts := &PromptSourceOptions{ @@ -61,6 +62,8 @@ func NewCmdSource(f *cmdutil.Factory) *cobra.Command { source_update.NewSourceUpdateCmd(f).Run(cmd, []string{}) case choices[4]: source_config.NewSourceConfigCmd(f).Run(cmd, []string{}) + case "Exit": + os.Exit(0) } }, } diff --git a/pkg/cmd/teams/teams.go b/pkg/cmd/teams/teams.go index f6e12dc..116f209 100644 --- a/pkg/cmd/teams/teams.go +++ b/pkg/cmd/teams/teams.go @@ -3,6 +3,9 @@ package teams import ( "errors" "fmt" + "net/http" + "os" + "github.com/logfire-sh/cli/internal/config" "github.com/logfire-sh/cli/internal/prompter" "github.com/logfire-sh/cli/pkg/cmd/teams/member_invite" @@ -16,7 +19,6 @@ import ( "github.com/logfire-sh/cli/pkg/cmdutil" "github.com/logfire-sh/cli/pkg/iostreams" "github.com/spf13/cobra" - "net/http" ) type PromptTeamsOptions struct { @@ -30,7 +32,7 @@ type PromptTeamsOptions struct { Choice string } -var choices = []string{"Create team", "List teams", "Delete team", "Update team", "Invite members", "List members", "Remove member", "Update member"} +var choices = []string{"Create team", "List teams", "Delete team", "Update team", "Invite members", "List members", "Remove member", "Update member", "Exit"} func NewCmdTeam(f *cmdutil.Factory) *cobra.Command { opts := &PromptTeamsOptions{ @@ -69,6 +71,8 @@ func NewCmdTeam(f *cmdutil.Factory) *cobra.Command { member_remove.NewMemberRemoveCmd(f).Run(cmd, []string{}) case choices[7]: member_update.NewMemberUpdateCmd(f).Run(cmd, []string{}) + case "Exit": + os.Exit(0) } }, } diff --git a/pkg/cmd/views/views.go b/pkg/cmd/views/views.go index d29bcb5..834e41e 100644 --- a/pkg/cmd/views/views.go +++ b/pkg/cmd/views/views.go @@ -3,6 +3,9 @@ package views import ( "errors" "fmt" + "net/http" + "os" + "github.com/logfire-sh/cli/internal/config" "github.com/logfire-sh/cli/internal/prompter" "github.com/logfire-sh/cli/pkg/cmd/views/views_delete" @@ -10,7 +13,6 @@ import ( "github.com/logfire-sh/cli/pkg/cmdutil" "github.com/logfire-sh/cli/pkg/iostreams" "github.com/spf13/cobra" - "net/http" ) type PromptViewsOptions struct { @@ -24,7 +26,7 @@ type PromptViewsOptions struct { Choice string } -var choices = []string{"List", "Delete"} +var choices = []string{"List", "Delete", "Exit"} func NewCmdViews(f *cmdutil.Factory) *cobra.Command { opts := &PromptViewsOptions{ @@ -51,6 +53,8 @@ func NewCmdViews(f *cmdutil.Factory) *cobra.Command { views_list.NewViewListCmd(f).Run(cmd, []string{}) case choices[1]: views_delete.NewDeleteCmd(f).Run(cmd, []string{}) + case "Exit": + os.Exit(0) } }, } diff --git a/pkg/cmdutil/APICalls/profile_api_calls.go b/pkg/cmdutil/APICalls/profile_api_calls.go index de34163..1e2dc9f 100644 --- a/pkg/cmdutil/APICalls/profile_api_calls.go +++ b/pkg/cmdutil/APICalls/profile_api_calls.go @@ -368,7 +368,7 @@ func OnboardingFlow(profileID, authToken, endpoint, firstName, lastName, role st return nil } -func TokenSignIn(cfg config.Config, token, endpoint string) error { +func TokenSignIn(cfg config.Config, token, endpoint string) (error, bool) { var response LoginModels.Response signinReq := LoginModels.SigninRequest{ @@ -381,14 +381,14 @@ func TokenSignIn(cfg config.Config, token, endpoint string) error { reqBody, err := json.Marshal(signinReq) if err != nil { fmt.Printf("Failed to marshal request body: %v\n", err) - return err + return err, false } url := endpoint + "api/auth/signin" req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody)) if err != nil { - return err + return err, false } req.Header.Set("User-Agent", "Logfire-cli") req.Header.Add("Content-Type", "application/json") @@ -400,33 +400,37 @@ func TokenSignIn(cfg config.Config, token, endpoint string) error { os.Exit(1) } - return err + return err, false } body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("Failed to read response body: %v\n", err) - return err + return err, false } err = json.Unmarshal(body, &response) if err != nil { fmt.Printf("Failed to unmarshal JSON: %v\n", err) - return err + return err, false } if !response.IsSuccessful { - return errors.New(response.Message[0]) + return errors.New(response.Message[0]), false } err = cfg.UpdateConfig(&response.UserBody.Email, &response.UserBody.Role, &response.BearerToken.AccessToken, &response.UserBody.ProfileID, &response.BearerToken.RefreshToken, nil, &response.UserBody.AccountID, nil, nil, nil, nil) if err != nil { fmt.Printf("Failed to update config: %v\n", err) - return err + return err, false } - return nil + if response.UserBody.Onboarded { + return nil, true + } + + return nil, false } func UpdateFlag(cfg config.Config, profileID, teamId, endpoint string) error { diff --git a/pkg/cmdutil/grpcutil/grpc.go b/pkg/cmdutil/grpcutil/grpc.go index 4ea0087..dbc4bc9 100644 --- a/pkg/cmdutil/grpcutil/grpc.go +++ b/pkg/cmdutil/grpcutil/grpc.go @@ -2,15 +2,17 @@ package grpcutil import ( "context" + "crypto/tls" + "crypto/x509" + "log" + "time" "github.com/logfire-sh/cli/internal/config" "github.com/logfire-sh/cli/pkg/cmd/sources/models" pb "github.com/logfire-sh/cli/services/flink-service" "google.golang.org/grpc" - - //"google.golang.org/grpc/credentials" - "log" - + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" ) @@ -30,25 +32,50 @@ func authUnaryInterceptor(kv ...string) func(ctx context.Context, method string, func (fs *FilterService) CloseConnection() { err := fs.conn.Close() if err != nil { - return + log.Printf("Failed to close connection: %v", err) } } func NewFilterService(kv ...string) *FilterService { cfg, _ := config.NewConfig() - grpc_url := cfg.Get().GrpcEndpoint - allParams := make([]string, 0, len(kv)+2) - allParams = append(allParams, "Authorization", "Bearer "+cfg.Get().Token) - allParams = append(allParams, kv...) + grpcURL := cfg.Get().GrpcEndpoint + allParams := append([]string{"Authorization", "Bearer " + cfg.Get().Token}, kv...) + + // Retry policy + backoffConfig := backoff.Config{ + BaseDelay: 1 * time.Second, + Multiplier: 1.6, + Jitter: 0.2, + MaxDelay: 5 * time.Second, + } + + // Load system CA certs + systemRoots, err := x509.SystemCertPool() + if err != nil { + log.Fatalf("Failed to load system cert pool: %v", err) + } + creds := credentials.NewTLS(&tls.Config{ + RootCAs: systemRoots, + }) + log.Printf("Connecting to gRPC server at %s", grpcURL) // conn, err := grpc.Dial(grpc_url, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), grpc.WithUnaryInterceptor(authUnaryInterceptor(allParams...)), grpc.WithUserAgent("Logfire-cli")) - conn, err := grpc.Dial(grpc_url, grpc.WithInsecure(), grpc.WithUnaryInterceptor(authUnaryInterceptor(allParams...)), grpc.WithUserAgent("Logfire-cli")) + // conn, err := grpc.Dial(grpc_url, grpc.WithInsecure(), grpc.WithUnaryInterceptor(authUnaryInterceptor(allParams...)), grpc.WithUserAgent("Logfire-cli")) + conn, err := grpc.Dial(grpcURL, + grpc.WithTransportCredentials(creds), + grpc.WithUnaryInterceptor(authUnaryInterceptor(allParams...)), + grpc.WithUserAgent("Logfire-cli"), + grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoffConfig, + MinConnectTimeout: 10 * time.Second, + }), + ) if err != nil { log.Fatalf("Failed to dial server: %v", err) } - // Create a gRPC client + log.Printf("Successfully connected to gRPC server") client := pb.NewFilterServiceClient(conn) return &FilterService{ diff --git a/pkg/cmdutil/grpcutil/onboarding_wait_for_log.go b/pkg/cmdutil/grpcutil/onboarding_wait_for_log.go index db0fbc5..f8c2e02 100644 --- a/pkg/cmdutil/grpcutil/onboarding_wait_for_log.go +++ b/pkg/cmdutil/grpcutil/onboarding_wait_for_log.go @@ -37,6 +37,15 @@ func GetLog(config config.Config, token, endpoint, teamId, accountId, sourceId, pbSources := CreateGrpcSource(sources) request.Sources = pbSources + // send test log + + istLocation, err := time.LoadLocation("UTC") + if err != nil { + log.Println("Error loading IST location:", err) + stop <- err + return + } + filterService := NewFilterService() defer filterService.CloseConnection() @@ -46,17 +55,11 @@ func GetLog(config config.Config, token, endpoint, teamId, accountId, sourceId, stop <- nil return default: - istLocation, err := time.LoadLocation("UTC") - if err != nil { - log.Println("Error loading IST location:", err) - stop <- err - return - } - currentTime := time.Now().In(istLocation) formattedTime := currentTime.Format("2006-01-02 15:04:05") ctxCmd, cancelCmd := context.WithTimeout(context.Background(), 5*time.Second) + defer cancelCmd() cmd := exec.CommandContext(ctxCmd, "curl", "--location", @@ -73,15 +76,10 @@ func GetLog(config config.Config, token, endpoint, teamId, accountId, sourceId, // Start the curl command if err := cmd.Start(); err != nil { log.Println("Error starting curl command:", err) - cancelCmd() stop <- err return } - // Allow the command to run for a short period before cancelling it - time.Sleep(1100 * time.Millisecond) - cancelCmd() - // Wait for the curl command to complete if err := cmd.Wait(); err != nil && err.Error() != "signal: killed" { log.Println("Error waiting for curl command:", err) @@ -89,6 +87,8 @@ func GetLog(config config.Config, token, endpoint, teamId, accountId, sourceId, stop <- err return } + // Allow the command to run for a short period before cancelling it + time.Sleep(1100 * time.Millisecond) // Check response from filter service response, err := filterService.Client.GetFilteredData(context.Background(), request)