Skip to content
This repository was archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
Merge in Ignite flow
Browse files Browse the repository at this point in the history
  • Loading branch information
Blake Watters committed May 27, 2020
2 parents 9635a0c + 582ce7e commit 8e4b6a4
Show file tree
Hide file tree
Showing 37 changed files with 4,556 additions and 61 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ snapshot:
release:
$(info ******************** releasing ********************)
goreleaser --rm-dist


5 changes: 5 additions & 0 deletions command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ func (cmd *BaseCommand) OutOrStdout() io.Writer {
return cmd.rootCobraCommand.OutOrStdout()
}

// ErrOrStderr returns output to stdout.
func (cmd *BaseCommand) ErrOrStderr() io.Writer {
return cmd.rootCobraCommand.ErrOrStderr()
}

// Print is a convenience method to Print to the defined output, fallback to Stderr if not set.
func (cmd *BaseCommand) Print(i ...interface{}) {
cmd.rootCobraCommand.Print(i...)
Expand Down
73 changes: 70 additions & 3 deletions command/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/go-resty/resty/v2"
"github.com/mgutz/ansi"
"github.com/spf13/cobra"
)
Expand All @@ -33,6 +34,66 @@ type initCommand struct {
confirmed bool
}

// type configResponse struct {
// BaseURL string `json:"base_url"`
// App string `json:"app"`
// Token string `json:"token"`
// }

func (initCmd *initCommand) RunInitWithTokenCommand(_ *cobra.Command, args []string) error {
initToken := args[0]

initCmd.Printf("Initializing with token: %s...\n", initToken)

configFile := initCmd.viperCfg.ConfigFileUsed()
// NOTE: On first launch with no config file, Viper returns ""
// because no config file was resolved. There's probably a cleaner solution...
if configFile == "" {
configFile = initCmd.DefaultConfigFile()
}
var profile Profile
URL := fmt.Sprintf("http://localhost:5678/init/%s", initToken)
client := resty.New()
resp, err := client.R().
SetResult(&profile).
Get(URL)
if err != nil {
return err
}
if !resp.IsSuccess() {
return fmt.Errorf("Failed initialization with token %q (%s)", initToken, resp.Body())
}

// Confirm that the user wants to write this config
registry := NewProfileRegistry(initCmd.viperCfg)
registry.AddProfile(profile)

initCmd.Printf("\nOpsani config initialized:\n")
initCmd.PrettyPrintYAMLObject(initCmd.GetAllSettings())
if !initCmd.confirmed {
prompt := &survey.Confirm{
Message: fmt.Sprintf("Write to %s?", configFile),
}
initCmd.AskOne(prompt, &initCmd.confirmed)
}
if initCmd.confirmed {
configDir := filepath.Dir(configFile)
if _, err := os.Stat(configDir); os.IsNotExist(err) {
err = os.Mkdir(configDir, 0755)
if err != nil {
return err
}
}
if err := initCmd.viperCfg.WriteConfigAs(configFile); err != nil {
return err
}
initCmd.Println("\nOpsani CLI initialized")
}
initCmd.Println("\nBegin optimizing by working with an interactive demo via `opsani ignite`")
initCmd.Println("Or jump right in to connecting your app by running `opsani vital`")
return nil
}

// RunInitCommand initializes Opsani CLI config
func (initCmd *initCommand) RunInitCommand(_ *cobra.Command, args []string) error {
// Handle reinitialization case
Expand Down Expand Up @@ -123,16 +184,22 @@ func (initCmd *initCommand) RunInitCommand(_ *cobra.Command, args []string) erro
func NewInitCommand(baseCommand *BaseCommand) *cobra.Command {
initCmd := &initCommand{BaseCommand: baseCommand}
cmd := &cobra.Command{
Use: "init",
Use: "init [INIT_TOKEN]",
Short: "Initialize Opsani config",
Long: `Initializes an Opsani config file and acquires the required settings:
* 'app': Opsani app to control (OPSANI_APP).
* 'token': API token to authenticate with (OPSANI_TOKEN).
`,
Args: cobra.NoArgs,
RunE: initCmd.RunInitCommand,
Args: cobra.MaximumNArgs(1),
PersistentPreRunE: initCmd.InitConfigRunE, // Skip loading the config file
RunE: func(c *cobra.Command, args []string) error {
if len(args) == 1 {
return initCmd.RunInitWithTokenCommand(c, args)
} else {
return initCmd.RunInitCommand(c, args)
}
},
}
cmd.Flags().BoolVar(&initCmd.confirmed, confirmedArg, false, "Write config without asking for confirmation")
return cmd
Expand Down
30 changes: 15 additions & 15 deletions command/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package command_test
import (
"fmt"
"io/ioutil"
"os"
"testing"

"github.com/AlecAivazis/survey/v2"
Expand Down Expand Up @@ -82,26 +83,21 @@ func (s *InitTestSuite) TestTerminalConfirm() {
s.Require().False(confirmed)
}

func (s *InitTestSuite) TestInitWithExistingConfigDeclinedL() {
configFile := test.TempConfigFileWithObj(map[string]interface{}{
"profiles": []map[string]string{
{
"app": "example.com/app",
"token": "123456",
},
},
})

context, err := s.ExecuteTestInteractively(test.Args("--config", configFile.Name(), "init"), func(t *test.InteractiveTestContext) error {
t.RequireStringf("Using config from: %s", configFile.Name())
t.RequireStringf("Existing config found. Overwrite %s?", configFile.Name())
func (s *InitTestSuite) TestInitWithExistingConfigDeclinedNoConfigFile() {
cfgName := "/tmp/this-will-never-exist.yaml"
os.Remove(cfgName)
context, err := s.ExecuteTestInteractively(test.Args("--config", cfgName, "init"), func(t *test.InteractiveTestContext) error {
t.ExpectMatch(expect.RegexpPattern("Opsani app"))
t.SendLine("dev.opsani.com/amazing-app")
t.RequireMatch(expect.RegexpPattern("API Token"))
t.SendLine("123456")
t.RequireMatch(expect.RegexpPattern(fmt.Sprintf("Write to %s?", cfgName)))
t.SendLine("N")
t.ExpectEOF()
return nil
})
s.T().Logf("The output buffer is: %v", context.OutputBuffer().String())
s.Require().Error(err)
s.Require().EqualError(err, terminal.InterruptErr.Error())
s.Require().NoError(err)
}

func (s *InitTestSuite) TestInitWithExistingConfigDeclined() {
Expand Down Expand Up @@ -165,3 +161,7 @@ func (s *InitTestSuite) TestInitWithExistingConfigAccepted() {
s.Require().Equal("dev.opsani.com/amazing-app", config.Profiles[1].App)
s.Require().Equal("123456", config.Profiles[1].Token)
}

func (s *InitTestSuite) TestInitWithToken() {
s.T().Skip("Pending test for init with a token")
}
8 changes: 4 additions & 4 deletions command/profile_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import (

// Profile represents an Opsani app, token, and base URL
type Profile struct {
Name string `yaml:"name" mapstructure:"name"`
App string `yaml:"app" mapstructure:"app"`
Token string `yaml:"token" mapstructure:"token"`
BaseURL string `yaml:"base_url" mapstructure:"base_url"`
Name string `yaml:"name" mapstructure:"name" json:"name"`
App string `yaml:"app" mapstructure:"app" json:"app"`
Token string `yaml:"token" mapstructure:"token" json:"token"`
BaseURL string `yaml:"base_url" mapstructure:"base_url" json:"base_url"`
}

// ProfileRegistry provides an interface for managing configuration of app profiles
Expand Down
47 changes: 47 additions & 0 deletions command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ import (
"regexp"
"runtime/debug"
"strings"
"time"

"github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/briandowns/spinner"
"github.com/docker/docker/pkg/term"
"github.com/fatih/color"
"github.com/mitchellh/go-homedir"
"github.com/opsani/cli/opsani"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -143,6 +146,9 @@ We'd love to hear your feedback at <https://github.com/opsani/cli>`,
cobraCmd.AddCommand(NewConfigCommand(rootCmd))
cobraCmd.AddCommand(NewCompletionCommand(rootCmd))

cobraCmd.AddCommand(NewVitalCommand(rootCmd))
cobraCmd.AddCommand(NewDemoCommand(rootCmd))

// Usage and help layout
cobra.AddTemplateFunc("hasSubCommands", hasSubCommands)
cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands)
Expand Down Expand Up @@ -318,6 +324,47 @@ func (baseCmd *BaseCommand) initConfig() error {
return nil
}

func (vitalCommand *vitalCommand) newSpinner() *spinner.Spinner {
s := spinner.New(spinner.CharSets[14], 150*time.Millisecond)
s.Writer = vitalCommand.OutOrStdout()
s.Color("bold", "blue")
s.HideCursor = true
return s
}

func (vitalCommand *vitalCommand) successMessage(message string) string {
c := color.New(color.FgGreen, color.Bold).SprintFunc()
return fmt.Sprintf("%s %s\n", c("\u2713"), message)
}

func (vitalCommand *vitalCommand) failureMessage(message string) string {
c := color.New(color.Bold, color.FgHiRed).SprintFunc()
return fmt.Sprintf("%s %s\n", c("\u2717"), message)
}

// Task describes a long-running task that may succeed or fail
type Task struct {
Description string
Success string
Failure string
Run func() error
}

// RunTaskWithSpinnerStatus displays an animated spinner around the execution of the given func
func (vitalCommand *vitalCommand) RunTaskWithSpinner(task Task) error {
s := vitalCommand.newSpinner()
s.Suffix = " " + task.Description
s.Start()
err := task.Run()
s.Stop()
if err == nil {
fmt.Fprintf(s.Writer, vitalCommand.successMessage(task.Success))
} else {
fmt.Fprintf(s.Writer, vitalCommand.failureMessage(task.Failure))
}
return err
}

// NewAPIClient returns an Opsani API client configured using the active configuration
func (baseCmd *BaseCommand) NewAPIClient() *opsani.Client {
c := opsani.NewClient().
Expand Down
Loading

0 comments on commit 8e4b6a4

Please sign in to comment.