Skip to content

fix: restore previous session on coder server --dev #1821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 27, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: restore previous session on coder server --dev
  • Loading branch information
f0ssel committed May 26, 2022
commit ba9724e963c043dd2e65233bf486557d81dcec6c
62 changes: 53 additions & 9 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,11 @@ func server() *cobra.Command {
return xerrors.Errorf("generate random admin password for dev: %w", err)
}
}
err = createFirstUser(cmd, client, config, devUserEmail, devUserPassword)
cleanupSession, err := createFirstUser(logger, cmd, client, config, devUserEmail, devUserPassword)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Would restore/restorePreviousSession be more descriptive here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if err != nil {
return xerrors.Errorf("create first user: %w", err)
}
defer cleanupSession()
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "email: %s\n", devUserEmail)
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "password: %s\n", devUserPassword)
_, _ = fmt.Fprintln(cmd.ErrOrStderr())
Expand Down Expand Up @@ -518,12 +519,14 @@ func server() *cobra.Command {
return root
}

func createFirstUser(cmd *cobra.Command, client *codersdk.Client, cfg config.Root, email, password string) error {
// createFirstUser creates the first user and sets a valid session.
// Caller must call cleanup on server exit.
func createFirstUser(log slog.Logger, cmd *cobra.Command, client *codersdk.Client, cfg config.Root, email, password string) (func(), error) {
if email == "" {
return xerrors.New("email is empty")
return nil, xerrors.New("email is empty")
}
if password == "" {
return xerrors.New("password is empty")
return nil, xerrors.New("password is empty")
}
_, err := client.CreateFirstUser(cmd.Context(), codersdk.CreateFirstUserRequest{
Email: email,
Expand All @@ -532,26 +535,67 @@ func createFirstUser(cmd *cobra.Command, client *codersdk.Client, cfg config.Roo
OrganizationName: "acme-corp",
})
if err != nil {
return xerrors.Errorf("create first user: %w", err)
return nil, xerrors.Errorf("create first user: %w", err)
}
token, err := client.LoginWithPassword(cmd.Context(), codersdk.LoginWithPasswordRequest{
Email: email,
Password: password,
})
if err != nil {
return xerrors.Errorf("login with first user: %w", err)
return nil, xerrors.Errorf("login with first user: %w", err)
}
client.SessionToken = token.SessionToken

oldUrl, err := cfg.URL().Read()
if err != nil {
return nil, xerrors.Errorf("write local url: %w", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would fail if an old session didn't exist, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}
oldSession, err := cfg.Session().Read()
if err != nil {
return nil, xerrors.Errorf("write session token: %w", err)
}

// recover session data when server exits
cleanup := func() {
currentUrl, err := cfg.URL().Read()
if err != nil {
log.Error(cmd.Context(), "failed to read current session url", slog.Error(err))
return
}
currentSession, err := cfg.Session().Read()
if err != nil {
log.Error(cmd.Context(), "failed to read current session token", slog.Error(err))
return
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought: Do we need to consider cases where the user has tried the logout flow or ran coder login against the dev server? If so we could consider renaming the files to {url,session}.bak, and if those files are present on exit, (or startup), restore them. Perhaps even dev tokens could be a different format from non-dev, that way we could always identify if a restore case is appropriate or not.

Being able to restore on startup would also cover cases where the server is killed instead of graciously exited.

I'm probably overthinking this though, this change alone should help alleviate the most common cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think as a first pass I'd rather not try to guess, because I could see devs spending quite a while trying to figure out what's wrong before they realize it's the session. Currently if it's different that we would expect we just choose "do nothing", and I'm down to add more logic here as we use it and run into cases.


// if it's changed since we wrote to it don't restore session
if currentUrl != client.URL.String() ||
currentSession != token.SessionToken {
return
}

err = cfg.URL().Write(oldUrl)
if err != nil {
log.Error(cmd.Context(), "failed to recover previous session url", slog.Error(err))
return
}
err = cfg.Session().Write(oldSession)
if err != nil {
log.Error(cmd.Context(), "failed to recover previous session token", slog.Error(err))
return
}
}

err = cfg.URL().Write(client.URL.String())
if err != nil {
return xerrors.Errorf("write local url: %w", err)
return nil, xerrors.Errorf("write local url: %w", err)
}
err = cfg.Session().Write(token.SessionToken)
if err != nil {
return xerrors.Errorf("write session token: %w", err)
return nil, xerrors.Errorf("write session token: %w", err)
}
return nil

return cleanup, nil
}

// nolint:revive
Expand Down