Skip to content

Commit

Permalink
debug
Browse files Browse the repository at this point in the history
Signed-off-by: Gabriel Mougard <[email protected]>
  • Loading branch information
gabrielmougard committed Sep 4, 2024
1 parent f5701e7 commit a011d9a
Show file tree
Hide file tree
Showing 7 changed files with 418 additions and 152 deletions.
4 changes: 2 additions & 2 deletions cmd/microcloud/ask.go
Original file line number Diff line number Diff line change
Expand Up @@ -1105,8 +1105,8 @@ func (c *initConfig) askOVNNetwork(sh *service.Handler) error {
}

canOVNUnderlay := true
for peer, state := range c.state {
if len(state.AvailableOVNInterfaces) == 0 {
for peer, system := range c.systems {
if len(c.state[system.ServerInfo.Name].AvailableOVNInterfaces) == 0 {
fmt.Printf("Not enough interfaces available on %s to create an underlay network, skipping\n", peer)
canOVNUnderlay = false
break
Expand Down
231 changes: 231 additions & 0 deletions cmd/microcloud/asker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package main

import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"

"golang.org/x/term"
)

func ValueInSlice[T comparable](key T, list []T) bool {
for _, entry := range list {
if entry == key {
return true
}
}

return false
}

// Asker holds a reader for reading input into CLI questions.
type Asker struct {
reader *bufio.Reader
logger *log.Logger
}

// NewAsker creates a new Asker instance that reads from the given reader.
// It can also be configured with a logger to help during the debug process.
func NewAsker(reader *bufio.Reader, logger *log.Logger) Asker {
return Asker{reader: reader, logger: logger}
}

// AskBool asks a question and expect a yes/no answer.
func (a *Asker) AskBool(question string, defaultAnswer string) (bool, error) {
for {
answer, err := a.askQuestion(question, defaultAnswer)
if err != nil {
if a.logger != nil {
a.logger.Printf("Failed to read answer (%v) for question (%v). err : %v", answer, question, err)
}

return false, err
}

if ValueInSlice(strings.ToLower(answer), []string{"yes", "y"}) {
return true, nil
} else if ValueInSlice(strings.ToLower(answer), []string{"no", "n"}) {
return false, nil
}

a.invalidInput(question, answer)
}
}

// AskChoice asks the user to select one of multiple options.
func (a *Asker) AskChoice(question string, choices []string, defaultAnswer string) (string, error) {
for {
answer, err := a.askQuestion(question, defaultAnswer)
if err != nil {
if a.logger != nil {
a.logger.Printf("Failed to read answer (%v) for question (%v). err : %v", answer, question, err)
}

return "", err
}

if ValueInSlice(answer, choices) {
return answer, nil
} else if a.logger != nil {
a.logger.Printf("Answer (%v) not among available choices (%v))", answer, choices)
}

a.invalidInput(question, answer)
}
}

// AskInt asks the user to enter an integer between a min and max value.
func (a *Asker) AskInt(question string, min int64, max int64, defaultAnswer string, validate func(int64) error) (int64, error) {
for {
answer, err := a.askQuestion(question, defaultAnswer)
if err != nil {
if a.logger != nil {
a.logger.Printf("Failed to read answer (%v) for question (%v). err : %v", answer, question, err)
}

return -1, err
}

result, err := strconv.ParseInt(answer, 10, 64)
if err != nil {
if a.logger != nil {
a.logger.Printf("Invalid answer (%v) for question (%v). err : %v", answer, question, err)
}

fmt.Fprintf(os.Stderr, "Invalid input: %v\n\n", err)
continue
}

if !((min == -1 || result >= min) && (max == -1 || result <= max)) {
if a.logger != nil {
a.logger.Printf("Answer (%v) out of range for question (%v)", answer, question)
}

fmt.Fprintf(os.Stderr, "Invalid input: out of range\n\n")
continue
}

if validate != nil {
err = validate(result)
if err != nil {
if a.logger != nil {
a.logger.Printf("Invalid answer (%v) for question (%v). err : %v", answer, question, err)
}

fmt.Fprintf(os.Stderr, "Invalid input: %v\n\n", err)
continue
}
}

return result, err
}
}

// AskString asks the user to enter a string, which optionally
// conforms to a validation function.
func (a *Asker) AskString(question string, defaultAnswer string, validate func(string) error) (string, error) {
for {
answer, err := a.askQuestion(question, defaultAnswer)
if err != nil {
if a.logger != nil {
a.logger.Printf("Failed to read answer (%v) for question (%v). err : %v", answer, question, err)
}

return "", err
}

if validate != nil {
err = validate(answer)
if err != nil {
if a.logger != nil {
a.logger.Printf("Invalid answer (%v) for question (%v). err : %v", answer, question, err)
}

fmt.Fprintf(os.Stderr, "Invalid input: %v\n\n", err)
continue
}

return answer, err
}

if len(answer) != 0 {
return answer, err
}

a.invalidInput(question, answer)
}
}

// AskPassword asks the user to enter a password.
func (a *Asker) AskPassword(question string) string {
for {
fmt.Print(question)

pwd, _ := term.ReadPassword(0)
fmt.Println("")
inFirst := string(pwd)
inFirst = strings.TrimSuffix(inFirst, "\n")

fmt.Print("Again: ")
pwd, _ = term.ReadPassword(0)
fmt.Println("")
inSecond := string(pwd)
inSecond = strings.TrimSuffix(inSecond, "\n")

// refuse empty password or if password inputs do not match
if len(inFirst) > 0 && inFirst == inSecond {
return inFirst
}

a.invalidInput(question, "*****")
}
}

// AskPasswordOnce asks the user to enter a password.
//
// It's the same as AskPassword, but it won't ask to enter it again.
func (a *Asker) AskPasswordOnce(question string) string {
for {
fmt.Print(question)
pwd, _ := term.ReadPassword(0)
fmt.Println("")

// refuse empty password
spwd := string(pwd)
if len(spwd) > 0 {
return spwd
}

a.invalidInput(question, "*****")
}
}

// Ask a question on the output stream and read the answer from the input stream.
func (a *Asker) askQuestion(question, defaultAnswer string) (string, error) {
fmt.Print(question)

return a.readAnswer(defaultAnswer)
}

// Read the user's answer from the input stream, trimming newline and providing a default.
func (a *Asker) readAnswer(defaultAnswer string) (string, error) {
answer, err := a.reader.ReadString('\n')
answer = strings.TrimSpace(strings.TrimSuffix(answer, "\n"))
if answer == "" {
answer = defaultAnswer
}

return answer, err
}

// Print an invalid input message on the error stream.
func (a *Asker) invalidInput(question string, answer string) {
if a.logger != nil {
a.logger.Printf("Invalid answer (%v) for question (%v)", answer, question)
}

fmt.Fprintf(os.Stderr, "Invalid input, try again.\n\n")
}
7 changes: 4 additions & 3 deletions cmd/microcloud/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ package main
import (
"bufio"
"fmt"
"log"
"os"

cli "github.com/canonical/lxd/shared/cmd"
"github.com/spf13/cobra"

"github.com/canonical/microcloud/microcloud/version"
Expand All @@ -23,7 +23,7 @@ type CmdControl struct {
FlagLogVerbose bool
FlagMicroCloudDir string

asker cli.Asker
asker Asker
}

func main() {
Expand All @@ -34,7 +34,8 @@ func main() {
}

// common flags.
commonCmd := CmdControl{asker: cli.NewAsker(bufio.NewReader(os.Stdin))}
logger := log.New(os.Stdout, "logger:", log.LstdFlags)
commonCmd := CmdControl{asker: NewAsker(bufio.NewReader(os.Stdin), logger)}

useTestConsole := os.Getenv("TEST_CONSOLE")
if useTestConsole == "1" {
Expand Down
3 changes: 1 addition & 2 deletions cmd/microcloud/main_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/canonical/lxd/lxd/util"
"github.com/canonical/lxd/shared"
lxdAPI "github.com/canonical/lxd/shared/api"
cli "github.com/canonical/lxd/shared/cmd"
"github.com/canonical/lxd/shared/logger"
"github.com/canonical/lxd/shared/revert"
"github.com/canonical/lxd/shared/validate"
Expand Down Expand Up @@ -70,7 +69,7 @@ type initConfig struct {
common *CmdControl

// asker is the CLI user input helper.
asker *cli.Asker
asker *Asker

// address is the cluster address of the local system.
address string
Expand Down
5 changes: 2 additions & 3 deletions cmd/microcloud/test_console.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (

"github.com/AlecAivazis/survey/v2/terminal"
"github.com/Netflix/go-expect"
cli "github.com/canonical/lxd/shared/cmd"
"github.com/creack/pty"
"github.com/hinshun/vt10x"
)
Expand All @@ -25,7 +24,7 @@ type testConsole struct {
}

// prepareTestAsker removes comments from the lines read from the given reader, and assigns them to the test console reader.
func prepareTestAsker(r io.Reader) cli.Asker {
func prepareTestAsker(r io.Reader) Asker {
sc := bufio.NewScanner(r)
b := bytes.Buffer{}
for sc.Scan() {
Expand All @@ -39,7 +38,7 @@ func prepareTestAsker(r io.Reader) cli.Asker {

reader = bufio.NewReader(bytes.NewReader(b.Bytes()))

return cli.NewAsker(reader)
return NewAsker(reader, nil)
}

// NewTestConsole creates a new testConsole, with an underlying expect.Console and virtual terminal.
Expand Down
2 changes: 0 additions & 2 deletions test/suites/add.sh
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ test_add_interactive() {
export SETUP_ZFS="yes"
export ZFS_FILTER="lxd_disk1"
export ZFS_WIPE="yes"
export OVN_UNDERLAY_NETWORK="no"
microcloud_interactive | lxc exec micro01 -- sh -c "microcloud add > out"
lxc exec micro01 -- tail -1 out | grep "MicroCloud is ready" -q

Expand All @@ -211,7 +210,6 @@ test_add_interactive() {
export SETUP_ZFS="no"
export SETUP_CEPH="no"
export SETUP_OVN="no"
export OVN_UNDERLAY_NETWORK="no"

lxc exec micro04 -- snap disable microcloud
microcloud_interactive | lxc exec micro01 -- sh -c "microcloud init > out"
Expand Down
Loading

0 comments on commit a011d9a

Please sign in to comment.